public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
@ 2024-02-20  6:41 Nickle Wang via groups.io
  2024-02-21  1:51 ` Chang, Abner via groups.io
  0 siblings, 1 reply; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-20  6:41 UTC (permalink / raw)
  To: devel; +Cc: Igor Kulchytskyy, Abner Chang, Nick Ramirez

implement Redfish HTTP protocol driver.

Signed-off-by: Nickle Wang <nicklew@nvidia.com>
Co-authored-by: Igor Kulchytskyy <igork@ami.com>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Igor Kulchytskyy <igork@ami.com>
Cc: Nick Ramirez <nramirez@nvidia.com>
---
 RedfishPkg/RedfishPkg.dec                     |    7 +-
 RedfishPkg/RedfishComponents.dsc.inc          |    3 +-
 RedfishPkg/RedfishPkg.dsc                     |    2 +
 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf  |   73 +
 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h   |  256 ++++
 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h    |   44 +
 .../RedfishHttpDxe/RedfishHttpOperation.h     |   76 +
 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c   |  667 ++++++++
 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c    | 1344 +++++++++++++++++
 .../RedfishHttpDxe/RedfishHttpOperation.c     |  693 +++++++++
 RedfishPkg/Redfish.fdf.inc                    |    3 +-
 11 files changed, 3164 insertions(+), 4 deletions(-)
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
 create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c

diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
index 9b424efdf3..114f8d2ad8 100644
--- a/RedfishPkg/RedfishPkg.dec
+++ b/RedfishPkg/RedfishPkg.dec
@@ -157,8 +157,11 @@
   # set to EFI_REST_EX_PROTOCOL.
   #
   gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT32|0x00001009
-  ## This is used to enable HTTP content encoding on Redfish communication.
-  gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLEAN|0x0000100A
+  #
+  # This PCD string is introduced for platform developer to set the encoding method supported by BMC Redfish.
+  # Currently only "None" and "gzip" are supported.
+  #
+  gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|VOID*|0x0000100A
   #
   # Use below PCDs to control Redfhs HTTP protocol.
   #
diff --git a/RedfishPkg/RedfishComponents.dsc.inc b/RedfishPkg/RedfishComponents.dsc.inc
index 464ffc8606..d6c5b73d7f 100644
--- a/RedfishPkg/RedfishComponents.dsc.inc
+++ b/RedfishPkg/RedfishComponents.dsc.inc
@@ -7,7 +7,7 @@
 # "RedfishDefines.dsc.inc".
 #
 # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 #
 #    SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -28,4 +28,5 @@
   RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
   RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
   MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
+  RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
 !endif
diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
index 25ed193182..5849e7cf9e 100644
--- a/RedfishPkg/RedfishPkg.dsc
+++ b/RedfishPkg/RedfishPkg.dsc
@@ -45,6 +45,8 @@
   UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
   RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/PlatformCredentialLibNull.inf
   RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/RedfishContentCodingLibNull.inf
+  ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
 
   # NULL instance of IPMI related library.
   IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
new file mode 100644
index 0000000000..c7dfdffacf
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
@@ -0,0 +1,73 @@
+## @file
+#  RedfishHttpDxe is the DXE driver which provides
+#  EdkIIRedfishHttpProtocol to EDK2 Redfish Feature
+#  drivers for HTTP operation.
+#
+#  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001000b
+  BASE_NAME                      = RedfishHttpDxe
+  FILE_GUID                      = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = RedfishHttpEntryPoint
+  UNLOAD_IMAGE                   = RedfishHttpDriverUnload
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 ARM AARCH64 RISCV64
+#
+
+[Sources]
+  RedfishHttpData.c
+  RedfishHttpData.h
+  RedfishHttpDxe.c
+  RedfishHttpDxe.h
+  RedfishHttpOperation.c
+  RedfishHttpOperation.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  NetworkPkg/NetworkPkg.dec
+  RedfishPkg/RedfishPkg.dec
+
+[LibraryClasses.ARM]
+  ArmSoftFloatLib
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  RedfishContentCodingLib
+  DebugLib
+  HttpLib
+  JsonLib
+  MemoryAllocationLib
+  PrintLib
+  RedfishDebugLib
+  ReportStatusCodeLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEdkIIRedfishHttpProtocolGuid             ## PRODUCED
+  gEdkIIRedfishCredentialProtocolGuid       ## CONSUMES
+  gEfiRestExProtocolGuid                    ## CONSUEMS
+
+[Pcd]
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond
+  gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled
+  gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding
+
+[Depex]
+  TRUE
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
new file mode 100644
index 0000000000..6be610142e
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
@@ -0,0 +1,256 @@
+/** @file
+  Definitions of RedfishHttpData
+
+  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_DATA_H_
+#define EDKII_REDFISH_HTTP_DATA_H_
+
+#include "RedfishHttpDxe.h"
+
+#define REDFISH_HTTP_DRIVER_SIGNATURE   SIGNATURE_32 ('r', 'f', 'h', 'p')
+#define REDFISH_HTTP_CACHE_SIGNATURE    SIGNATURE_32 ('r', 'f', 'c', 'h')
+#define REDFISH_HTTP_SERVICE_SIGNATURE  SIGNATURE_32 ('r', 'f', 's', 'v')
+#define REDFISH_HTTP_PAYLOAD_SIGNATURE  SIGNATURE_32 ('r', 'f', 'p', 'l')
+#define REDFISH_HTTP_BASIC_AUTH_STR     "Basic "
+
+///
+/// REDFISH_SERVICE_PRIVATE definition.
+///
+typedef struct {
+  UINT32                  Signature;
+  CHAR8                   *Host;
+  CHAR8                   *HostName;
+  CHAR8                   *BasicAuth;
+  CHAR8                   *SessionToken;
+  EFI_REST_EX_PROTOCOL    *RestEx;
+} REDFISH_SERVICE_PRIVATE;
+
+///
+/// REDFISH_PAYLOAD_PRIVATE definition.
+///
+typedef struct {
+  UINT32                     Signature;
+  REDFISH_SERVICE_PRIVATE    *Service;
+  EDKII_JSON_VALUE           JsonValue;
+} REDFISH_PAYLOAD_PRIVATE;
+
+///
+/// Definition of REDFISH_HTTP_CACHE_DATA
+///
+typedef struct {
+  UINT32              Signature;
+  LIST_ENTRY          List;
+  EFI_STRING          Uri;
+  UINTN               HitCount;
+  REDFISH_RESPONSE    *Response;
+} REDFISH_HTTP_CACHE_DATA;
+
+#define REDFISH_HTTP_CACHE_FROM_LIST(a)  CR (a, REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE)
+
+///
+/// Definition of REDFISH_HTTP_CACHE_LIST
+///
+typedef struct {
+  LIST_ENTRY    Head;
+  UINTN         Count;
+  UINTN         Capacity;
+} REDFISH_HTTP_CACHE_LIST;
+
+///
+/// Definition of REDFISH_HTTP_RETRY_SETTING
+///
+typedef struct {
+  UINT16    MaximumRetryGet;
+  UINT16    MaximumRetryPut;
+  UINT16    MaximumRetryPost;
+  UINT16    MaximumRetryPatch;
+  UINT16    MaximumRetryDelete;
+  UINTN     RetryWait;
+} REDFISH_HTTP_RETRY_SETTING;
+
+///
+/// Definition of REDFISH_HTTP_CACHE_PRIVATE
+///
+typedef struct {
+  UINT32                               Signature;
+  EFI_HANDLE                           ImageHandle;
+  BOOLEAN                              CacheDisabled;
+  EFI_EVENT                            NotifyEvent;
+  REDFISH_HTTP_CACHE_LIST              CacheList;
+  EDKII_REDFISH_HTTP_PROTOCOL          Protocol;
+  EDKII_REDFISH_CREDENTIAL_PROTOCOL    *CredentialProtocol;
+  REDFISH_HTTP_RETRY_SETTING           RetrySetting;
+} REDFISH_HTTP_CACHE_PRIVATE;
+
+#define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a)  CR (a, REDFISH_HTTP_CACHE_PRIVATE, Protocol, REDFISH_HTTP_DRIVER_SIGNATURE)
+
+/**
+  Search on given ListHeader for given URI string.
+
+  @param[in]    ListHeader  Target list to search.
+  @param[in]    Uri         Target URI to search.
+
+  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found.
+  @retval NULL                      No cache data with given URI is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindHttpCacheData (
+  IN  LIST_ENTRY  *ListHeader,
+  IN  EFI_STRING  Uri
+  );
+
+/**
+  This function copy the data in SrcResponse to DstResponse.
+
+  @param[in]  SrcResponse      Source Response to copy.
+  @param[out] DstResponse      Destination Response.
+
+  @retval     EFI_SUCCESS      Response is copied successfully.
+  @retval     Others           Error occurs.
+
+**/
+EFI_STATUS
+CopyRedfishResponse (
+  IN  REDFISH_RESPONSE  *SrcResponse,
+  OUT REDFISH_RESPONSE  *DstResponse
+  );
+
+/**
+  Release all cache from list.
+
+  @param[in]    CacheList    The list to be released.
+
+  @retval EFI_SUCCESS             All cache data are released.
+  @retval EFI_INVALID_PARAMETER   CacheList is NULL.
+
+**/
+EFI_STATUS
+ReleaseCacheList (
+  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
+  );
+
+/**
+  Add new cache by given URI and HTTP response to specify List.
+
+  @param[in]    List      Target cache list to add.
+  @param[in]    Uri       The URI string matching to this cache data.
+  @param[in]    Response  HTTP response.
+
+  @retval EFI_SUCCESS   Cache data is added.
+  @retval Others        Fail to add cache data.
+
+**/
+EFI_STATUS
+AddHttpCacheData (
+  IN  REDFISH_HTTP_CACHE_LIST  *List,
+  IN  EFI_STRING               Uri,
+  IN  REDFISH_RESPONSE         *Response
+  );
+
+/**
+  Delete a cache data by given cache instance.
+
+  @param[in]    List    Target cache list to be removed.
+  @param[in]    Data    Pointer to the instance to be deleted.
+
+  @retval EFI_SUCCESS   Cache data is removed.
+  @retval Others        Fail to remove cache data.
+
+**/
+EFI_STATUS
+DeleteHttpCacheData (
+  IN  REDFISH_HTTP_CACHE_LIST  *List,
+  IN  REDFISH_HTTP_CACHE_DATA  *Data
+  );
+
+/**
+  This function release Redfish Payload.
+
+  @param[in]  Payload         Pointer to payload instance.
+
+  @retval     EFI_SUCCESS     Payload is released.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishPayload (
+  IN REDFISH_PAYLOAD_PRIVATE  *Payload
+  );
+
+/**
+  This function creat new payload. Server and JsonObj are
+  copied to newly created payload.
+
+  @param[in]  Service          Pointer to Service instance.
+  @param[in]  JsonObj          Pointer to JSON object.
+
+  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload.
+  @retval     NULL             Error occurs.
+
+**/
+REDFISH_PAYLOAD_PRIVATE *
+CreateRedfishPayload (
+  IN REDFISH_SERVICE_PRIVATE  *Service,
+  IN EDKII_JSON_VALUE         JsonValue
+  );
+
+/**
+  This function release Redfish Service.
+
+  @param[in]  Service         Pointer to service instance.
+
+  @retval     EFI_SUCCESS     Service is released.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishService (
+  IN REDFISH_SERVICE_PRIVATE  *Service
+  );
+
+/**
+  This function creat new service. Host and HostName are copied to
+  newly created service instance.
+
+  @param[in]  Host            Host string.
+  @param[in]  HostName        Hostname string.
+  @param[in]  BasicAuth       Basic Authorization string.
+  @param[in]  SessionToken    Session token string.
+  @param[in]  RestEx          Rest EX protocol instance.
+
+  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service.
+  @retval     NULL             Error occurs.
+
+**/
+REDFISH_SERVICE_PRIVATE *
+CreateRedfishService (
+  IN CHAR8                 *Host,
+  IN CHAR8                 *HostName,
+  IN CHAR8                 *BasicAuth OPTIONAL,
+  IN CHAR8                 *SessionToken OPTIONAL,
+  IN EFI_REST_EX_PROTOCOL  *RestEx
+  );
+
+/**
+  This function update session token in Redfish Service.
+
+  @param[in]  Service         Pointer to service instance.
+  @param[in]  Token           Session token.
+
+  @retval     EFI_SUCCESS     Session token is updated.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+UpdateSessionToken (
+  IN REDFISH_SERVICE_PRIVATE  *Service,
+  IN CHAR8                    *Token
+  );
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
new file mode 100644
index 0000000000..cf6ba9cb47
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
@@ -0,0 +1,44 @@
+/** @file
+  Definitions of RedfishHttpDxe
+
+  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_DXE_H_
+#define EDKII_REDFISH_HTTP_DXE_H_
+
+#include <Uefi.h>
+#include <IndustryStandard/Http11.h>
+
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/RedfishContentCodingLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HttpLib.h>
+#include <Library/JsonLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/RedfishDebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/PrintLib.h>
+
+#include <Protocol/Http.h>
+#include <Protocol/EdkIIRedfishHttpProtocol.h>
+#include <Protocol/EdkIIRedfishCredential.h>
+#include <Protocol/RestEx.h>
+
+#define IS_EMPTY_STRING(a)  ((a) == NULL || (a)[0] == '\0')
+#define REDFISH_HTTP_CACHE_LIST_SIZE      0x80
+#define REDFISH_ERROR_MSG_MAX             128
+#define REDFISH_DEBUG_STRING_LENGTH       200
+#define REDFISH_HOST_NAME_MAX             64   // IPv6 maximum length (39) + "https://" (8) + port number (maximum 5)
+#define REDFISH_HTTP_ERROR_REPORT         "Redfish HTTP %a failure(0x%x): %s"
+#define REDFISH_HTTP_CACHE_DEBUG          DEBUG_MANAGEABILITY
+#define REDFISH_HTTP_CACHE_DEBUG_DUMP     DEBUG_MANAGEABILITY
+#define REDFISH_HTTP_CACHE_DEBUG_REQUEST  DEBUG_MANAGEABILITY
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
new file mode 100644
index 0000000000..e4fc053e96
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
@@ -0,0 +1,76 @@
+/** @file
+  Definitions of RedfishHttpOperation
+
+  Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_OP_H_
+#define EDKII_REDFISH_HTTP_OP_H_
+
+#include "RedfishHttpDxe.h"
+
+#define REDFISH_CONTENT_LENGTH_SIZE              80
+#define REDFISH_COMMON_HEADER_SIZE               5
+#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR    "OData-Version"
+#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE  "4.0"
+#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE     "edk2redfish"
+#define REDFISH_HTTP_HEADER_CONNECTION_STR       "Connection"
+#define REDFISH_HTTP_HEADER_CONNECTION_VALUE     "Keep-Alive"
+#define REDFISH_HTTP_CONTENT_ENCODING_NONE       "None"
+
+/**
+  This function free resources in Request. Request is no longer available
+  after this function returns successfully.
+
+  @param[in]  Request      HTTP request to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishRequest (
+  IN  REDFISH_REQUEST  *Request
+  );
+
+/**
+  This function free resources in given Response.
+
+  @param[in]  Response     HTTP response to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishResponse (
+  IN  REDFISH_RESPONSE  *Response
+  );
+
+/**
+  This function send Redfish request to Redfish service by calling
+  Rest Ex protocol.
+
+  @param[in]   Service       Pointer to Redfish service.
+  @param[in]   Uri           Uri of Redfish service.
+  @param[in]   Method        HTTP method.
+  @param[in]   Request     Request data. This is optional.
+  @param[out]  Response    Redfish response data.
+
+  @retval     EFI_SUCCESS     Request is sent and received successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+HttpSendReceive (
+  IN  REDFISH_SERVICE   Service,
+  IN  EFI_STRING        Uri,
+  IN  EFI_HTTP_METHOD   Method,
+  IN  REDFISH_REQUEST   *Request  OPTIONAL,
+  OUT REDFISH_RESPONSE  *Response
+  );
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
new file mode 100644
index 0000000000..bf95e9f8d4
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
@@ -0,0 +1,667 @@
+/** @file
+  RedfishHttpData handles internal data to support Redfish HTTP protocol.
+
+  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpData.h"
+#include "RedfishHttpOperation.h"
+
+/**
+  This function update session token in Redfish Service.
+
+  @param[in]  Service         Pointer to service instance.
+  @param[in]  Token           Session token.
+
+  @retval     EFI_SUCCESS     Session token is updated.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+UpdateSessionToken (
+  IN REDFISH_SERVICE_PRIVATE  *Service,
+  IN CHAR8                    *Token
+  )
+{
+  if ((Service == NULL) || IS_EMPTY_STRING (Token)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Service->SessionToken != NULL) {
+    FreePool (Service->SessionToken);
+  }
+
+  Service->SessionToken = AllocateCopyPool (AsciiStrSize (Token), Token);
+  if (Service->SessionToken == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function release Redfish Service.
+
+  @param[in]  Service         Pointer to service instance.
+
+  @retval     EFI_SUCCESS     Service is released.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishService (
+  IN REDFISH_SERVICE_PRIVATE  *Service
+  )
+{
+  if (Service == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Service->Host != NULL) {
+    FreePool (Service->Host);
+  }
+
+  if (Service->HostName != NULL) {
+    FreePool (Service->HostName);
+  }
+
+  if (Service->BasicAuth != NULL) {
+    ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth));
+    FreePool (Service->BasicAuth);
+  }
+
+  if (Service->SessionToken != NULL) {
+    ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken));
+    FreePool (Service->SessionToken);
+  }
+
+  FreePool (Service);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function creat new service. Host and HostName are copied to
+  newly created service instance.
+
+  @param[in]  Host            Host string.
+  @param[in]  HostName        Hostname string.
+  @param[in]  BasicAuth       Basic Authorization string.
+  @param[in]  SessionToken    Session token string.
+  @param[in]  RestEx          Rest EX protocol instance.
+
+  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service.
+  @retval     NULL             Error occurs.
+
+**/
+REDFISH_SERVICE_PRIVATE *
+CreateRedfishService (
+  IN CHAR8                 *Host,
+  IN CHAR8                 *HostName,
+  IN CHAR8                 *BasicAuth OPTIONAL,
+  IN CHAR8                 *SessionToken OPTIONAL,
+  IN EFI_REST_EX_PROTOCOL  *RestEx
+  )
+{
+  REDFISH_SERVICE_PRIVATE  *NewService;
+  UINTN                    AuthStrSize;
+
+  if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx == NULL)) {
+    return NULL;
+  }
+
+  NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE));
+  if (NewService == NULL) {
+    return NULL;
+  }
+
+  NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE;
+  NewService->Host      = AllocateCopyPool (AsciiStrSize (Host), Host);
+  if (NewService->Host == NULL) {
+    goto ON_ERROR;
+  }
+
+  NewService->HostName = AllocateCopyPool (AsciiStrSize (HostName), HostName);
+  if (NewService->HostName == NULL) {
+    goto ON_ERROR;
+  }
+
+  if (!IS_EMPTY_STRING (BasicAuth)) {
+    AuthStrSize           = AsciiStrSize (BasicAuth) + AsciiStrLen (REDFISH_HTTP_BASIC_AUTH_STR);
+    NewService->BasicAuth = AllocateZeroPool (AuthStrSize);
+    if (NewService->BasicAuth == NULL) {
+      goto ON_ERROR;
+    }
+
+    AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a", REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth);
+  }
+
+  if (!IS_EMPTY_STRING (SessionToken)) {
+    NewService->SessionToken = AllocateCopyPool (AsciiStrSize (SessionToken), SessionToken);
+    if (NewService->SessionToken == NULL) {
+      goto ON_ERROR;
+    }
+  }
+
+  NewService->RestEx = RestEx;
+
+  return NewService;
+
+ON_ERROR:
+
+  ReleaseRedfishService (NewService);
+
+  return NULL;
+}
+
+/**
+  This function release Redfish Payload.
+
+  @param[in]  Payload         Pointer to payload instance.
+
+  @retval     EFI_SUCCESS     Payload is released.
+  @retval     Others          Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishPayload (
+  IN REDFISH_PAYLOAD_PRIVATE  *Payload
+  )
+{
+  if (Payload == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Payload->Service != NULL) {
+    ReleaseRedfishService (Payload->Service);
+  }
+
+  if (Payload->JsonValue != NULL) {
+    JsonValueFree (Payload->JsonValue);
+  }
+
+  FreePool (Payload);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function creat new payload. Server and JsonObj are
+  copied to newly created payload.
+
+  @param[in]  Service          Pointer to Service instance.
+  @param[in]  JsonValue        Pointer to JSON value.
+
+  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload.
+  @retval     NULL                     Error occurs.
+
+**/
+REDFISH_PAYLOAD_PRIVATE *
+CreateRedfishPayload (
+  IN REDFISH_SERVICE_PRIVATE  *Service,
+  IN EDKII_JSON_VALUE         JsonValue
+  )
+{
+  REDFISH_PAYLOAD_PRIVATE  *NewPayload;
+
+  if ((Service == NULL) || (JsonValue == NULL)) {
+    return NULL;
+  }
+
+  NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE));
+  if (NewPayload == NULL) {
+    return NULL;
+  }
+
+  NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE;
+  NewPayload->Service   = CreateRedfishService (Service->Host, Service->HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx);
+  if (NewPayload->Service == NULL) {
+    goto ON_ERROR;
+  }
+
+  NewPayload->JsonValue = JsonValueClone (JsonValue);
+  if (NewPayload->JsonValue == NULL) {
+    goto ON_ERROR;
+  }
+
+  return NewPayload;
+
+ON_ERROR:
+
+  ReleaseRedfishPayload (NewPayload);
+
+  return NULL;
+}
+
+/**
+  This function copy the data in SrcResponse to DstResponse.
+
+  @param[in]  SrcResponse      Source Response to copy.
+  @param[out] DstResponse      Destination Response.
+
+  @retval     EFI_SUCCESS      Response is copied successfully.
+  @retval     Others           Error occurs.
+
+**/
+EFI_STATUS
+CopyRedfishResponse (
+  IN  REDFISH_RESPONSE  *SrcResponse,
+  OUT REDFISH_RESPONSE  *DstResponse
+  )
+{
+  REDFISH_PAYLOAD_PRIVATE  *Payload;
+  UINTN                    Index;
+
+  if ((SrcResponse == NULL) || (DstResponse == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (SrcResponse == DstResponse) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Status code
+  //
+  if (SrcResponse->StatusCode != NULL) {
+    DstResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
+    if (DstResponse->StatusCode == NULL) {
+      goto ON_ERROR;
+    }
+  }
+
+  //
+  // Header
+  //
+  if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
+    DstResponse->HeaderCount = 0;
+    DstResponse->Headers     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcResponse->HeaderCount);
+    if (DstResponse->Headers == NULL) {
+      goto ON_ERROR;
+    }
+
+    for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
+      DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldName), SrcResponse->Headers[Index].FieldName);
+      if (DstResponse->Headers[Index].FieldName == NULL) {
+        goto ON_ERROR;
+      }
+
+      DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldValue), SrcResponse->Headers[Index].FieldValue);
+      if (DstResponse->Headers[Index].FieldValue == NULL) {
+        goto ON_ERROR;
+      }
+
+      DstResponse->HeaderCount += 1;
+    }
+  }
+
+  //
+  // Payload
+  //
+  if (SrcResponse->Payload != NULL) {
+    Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload;
+    if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
+      DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+      goto ON_ERROR;
+    }
+
+    DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload->JsonValue);
+    if (DstResponse->Payload  == NULL) {
+      goto ON_ERROR;
+    }
+  }
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+
+  ReleaseRedfishResponse (DstResponse);
+
+  return EFI_OUT_OF_RESOURCES;
+}
+
+/**
+  This function clone input response and return to caller
+
+  @param[in]  Response      Response to clone.
+
+  @retval     REDFISH_RESPONSE *  Response is cloned.
+  @retval     NULL                Errors occur.
+
+**/
+REDFISH_RESPONSE *
+CloneRedfishResponse (
+  IN REDFISH_RESPONSE  *Response
+  )
+{
+  EFI_STATUS        Status;
+  REDFISH_RESPONSE  *NewResponse;
+
+  if (Response == NULL) {
+    return NULL;
+  }
+
+  NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
+  if (NewResponse == NULL) {
+    return NULL;
+  }
+
+  Status = CopyRedfishResponse (Response, NewResponse);
+  if (EFI_ERROR (Status)) {
+    FreePool (NewResponse);
+    return NULL;
+  }
+
+  return NewResponse;
+}
+
+/**
+  Release REDFISH_HTTP_CACHE_DATA resource
+
+  @param[in]    Data    Pointer to REDFISH_HTTP_CACHE_DATA instance
+
+  @retval EFI_SUCCESS             REDFISH_HTTP_CACHE_DATA is released successfully.
+  @retval EFI_INVALID_PARAMETER   Data is NULL
+
+**/
+EFI_STATUS
+ReleaseHttpCacheData (
+  IN REDFISH_HTTP_CACHE_DATA  *Data
+  )
+{
+  if (Data == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Data->Uri != NULL) {
+    FreePool (Data->Uri);
+  }
+
+  if (Data->Response != NULL) {
+    ReleaseRedfishResponse (Data->Response);
+    FreePool (Data->Response);
+  }
+
+  FreePool (Data);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Create new cache data.
+
+  @param[in]    Uri       The URI string matching to this cache data.
+  @param[in]    Response  HTTP response.
+
+  @retval REDFISH_HTTP_CACHE_DATA *   Pointer to newly created cache data.
+  @retval NULL                        No memory available.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+NewHttpCacheData (
+  IN  EFI_STRING        Uri,
+  IN  REDFISH_RESPONSE  *Response
+  )
+{
+  REDFISH_HTTP_CACHE_DATA  *NewData;
+  UINTN                    Size;
+
+  if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+    return NULL;
+  }
+
+  NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
+  if (NewData == NULL) {
+    return NULL;
+  }
+
+  NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE;
+  Size               = StrSize (Uri);
+  NewData->Uri       = AllocateCopyPool (Size, Uri);
+  if (NewData->Uri == NULL) {
+    goto ON_ERROR;
+  }
+
+  NewData->Response = Response;
+  NewData->HitCount = 1;
+
+  return NewData;
+
+ON_ERROR:
+
+  if (NewData != NULL) {
+    ReleaseHttpCacheData (NewData);
+  }
+
+  return NULL;
+}
+
+/**
+  Search on given ListHeader for given URI string.
+
+  @param[in]    ListHeader  Target list to search.
+  @param[in]    Uri         Target URI to search.
+
+  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found.
+  @retval NULL                      No cache data with given URI is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindHttpCacheData (
+  IN  LIST_ENTRY  *ListHeader,
+  IN  EFI_STRING  Uri
+  )
+{
+  LIST_ENTRY               *List;
+  REDFISH_HTTP_CACHE_DATA  *Data;
+
+  if (IS_EMPTY_STRING (Uri)) {
+    return NULL;
+  }
+
+  if (IsListEmpty (ListHeader)) {
+    return NULL;
+  }
+
+  Data = NULL;
+  List = GetFirstNode (ListHeader);
+  while (!IsNull (ListHeader, List)) {
+    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+    if (StrCmp (Data->Uri, Uri) == 0) {
+      return Data;
+    }
+
+    List = GetNextNode (ListHeader, List);
+  }
+
+  return NULL;
+}
+
+/**
+  Search on given ListHeader and return cache data with minimum hit count.
+
+  @param[in]    ListHeader  Target list to search.
+
+  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is returned.
+  @retval NULL                      No cache data is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindUnusedHttpCacheData (
+  IN  LIST_ENTRY  *ListHeader
+  )
+{
+  LIST_ENTRY               *List;
+  REDFISH_HTTP_CACHE_DATA  *Data;
+  REDFISH_HTTP_CACHE_DATA  *UnusedData;
+  UINTN                    HitCount;
+
+  if (IsListEmpty (ListHeader)) {
+    return NULL;
+  }
+
+  Data       = NULL;
+  UnusedData = NULL;
+  HitCount   = 0;
+
+  List       = GetFirstNode (ListHeader);
+  Data       = REDFISH_HTTP_CACHE_FROM_LIST (List);
+  UnusedData = Data;
+  HitCount   = Data->HitCount;
+  List       = GetNextNode (ListHeader, List);
+
+  while (!IsNull (ListHeader, List)) {
+    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+    if (Data->HitCount < HitCount) {
+      HitCount   = Data->HitCount;
+      UnusedData = Data;
+    }
+
+    List = GetNextNode (ListHeader, List);
+  }
+
+  return UnusedData;
+}
+
+/**
+  Delete a cache data by given cache instance.
+
+  @param[in]    List    Target cache list to be removed.
+  @param[in]    Data    Pointer to the instance to be deleted.
+
+  @retval EFI_SUCCESS   Cache data is removed.
+  @retval Others        Fail to remove cache data.
+
+**/
+EFI_STATUS
+DeleteHttpCacheData (
+  IN  REDFISH_HTTP_CACHE_LIST  *List,
+  IN  REDFISH_HTTP_CACHE_DATA  *Data
+  )
+{
+  if ((List == NULL) || (Data == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__, Data->Uri));
+
+  RemoveEntryList (&Data->List);
+  --List->Count;
+
+  return ReleaseHttpCacheData (Data);
+}
+
+/**
+  Add new cache by given URI and HTTP response to specify List.
+
+  @param[in]    List      Target cache list to add.
+  @param[in]    Uri       The URI string matching to this cache data.
+  @param[in]    Response  HTTP response.
+
+  @retval EFI_SUCCESS   Cache data is added.
+  @retval Others        Fail to add cache data.
+
+**/
+EFI_STATUS
+AddHttpCacheData (
+  IN  REDFISH_HTTP_CACHE_LIST  *List,
+  IN  EFI_STRING               Uri,
+  IN  REDFISH_RESPONSE         *Response
+  )
+{
+  REDFISH_HTTP_CACHE_DATA  *NewData;
+  REDFISH_HTTP_CACHE_DATA  *OldData;
+  REDFISH_HTTP_CACHE_DATA  *UnusedData;
+  REDFISH_RESPONSE         *NewResponse;
+
+  if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If same cache data exist, replace it with latest one.
+  //
+  OldData = FindHttpCacheData (&List->Head, Uri);
+  if (OldData != NULL) {
+    DeleteHttpCacheData (List, OldData);
+  }
+
+  //
+  // Check capacity
+  //
+  if (List->Count >= List->Capacity) {
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused cache\n", __func__));
+    UnusedData = FindUnusedHttpCacheData (&List->Head);
+    if (UnusedData == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    DeleteHttpCacheData (List, UnusedData);
+  }
+
+  //
+  // Clone a local copy
+  //
+  NewResponse = CloneRedfishResponse (Response);
+  if (NewResponse == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  NewData = NewHttpCacheData (Uri, NewResponse);
+  if (NewData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InsertTailList (&List->Head, &NewData->List);
+  ++List->Count;
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n", __func__, List->Count, List->Capacity, NewData->Uri));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Release all cache from list.
+
+  @param[in]    CacheList    The list to be released.
+
+  @retval EFI_SUCCESS             All cache data are released.
+  @retval EFI_INVALID_PARAMETER   CacheList is NULL.
+
+**/
+EFI_STATUS
+ReleaseCacheList (
+  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
+  )
+{
+  LIST_ENTRY               *List;
+  LIST_ENTRY               *Next;
+  REDFISH_HTTP_CACHE_DATA  *Data;
+
+  if (CacheList == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IsListEmpty (&CacheList->Head)) {
+    return EFI_SUCCESS;
+  }
+
+  Data = NULL;
+  Next = NULL;
+  List = GetFirstNode (&CacheList->Head);
+  while (!IsNull (&CacheList->Head, List)) {
+    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+    Next = GetNextNode (&CacheList->Head, List);
+
+    DeleteHttpCacheData (CacheList, Data);
+
+    List = Next;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
new file mode 100644
index 0000000000..39958d4865
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
@@ -0,0 +1,1344 @@
+/** @file
+  RedfishHttpDxe produces EdkIIRedfishHttpProtocol
+  for EDK2 Redfish Feature driver to do HTTP operations.
+
+  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpDxe.h"
+#include "RedfishHttpData.h"
+#include "RedfishHttpOperation.h"
+
+REDFISH_HTTP_CACHE_PRIVATE  *mRedfishHttpCachePrivate = NULL;
+
+/**
+  Debug output the cache list.
+
+  @param[in]    Msg            Debug message string.
+  @param[in]    ErrorLevel     Output error level.
+  @param[in]    CacheList      Target list to dump.
+
+  @retval EFI_SUCCESS             Debug dump finished.
+  @retval EFI_INVALID_PARAMETER   HttpCacheList is NULL.
+
+**/
+EFI_STATUS
+DebugPrintHttpCacheList (
+  IN  CONST CHAR8              *Msg,
+  IN  UINTN                    ErrorLevel,
+  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
+  )
+{
+  LIST_ENTRY               *List;
+  REDFISH_HTTP_CACHE_DATA  *Data;
+  UINTN                    Index;
+
+  if (CacheList == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IS_EMPTY_STRING (Msg)) {
+    DEBUG ((ErrorLevel, "%a\n", Msg));
+  }
+
+  if (IsListEmpty (&CacheList->Head)) {
+    DEBUG ((ErrorLevel, "list is empty\n"));
+    return EFI_NOT_FOUND;
+  }
+
+  DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count, CacheList->Capacity));
+  Data  = NULL;
+  Index = 0;
+  List  = GetFirstNode (&CacheList->Head);
+  while (!IsNull (&CacheList->Head, List)) {
+    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+    DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data->HitCount));
+
+    List = GetNextNode (&CacheList->Head, List);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+  Check HTTP status code to see if we like to retry HTTP request or not.
+
+  @param[in]  StatusCode      HTTP status code.
+
+  @retval     BOOLEAN         Return true when we like to retry request.
+                              Return false when we don't want to retry request.
+
+**/
+BOOLEAN
+RedfishRetryRequired (
+  IN EFI_HTTP_STATUS_CODE  *StatusCode
+  )
+{
+  if (StatusCode == NULL) {
+    return TRUE;
+  }
+
+  if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) ||
+      (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS))
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+
+  Convert Unicode string to ASCII string. It's call responsibility to release returned buffer.
+
+  @param[in]  UnicodeStr      Unicode string to convert.
+
+  @retval     CHAR8 *         ASCII string returned.
+  @retval     NULL            Errors occur.
+
+**/
+CHAR8 *
+StringUnicodeToAscii (
+  IN EFI_STRING  UnicodeStr
+  )
+{
+  CHAR8       *AsciiStr;
+  UINTN       AsciiStrSize;
+  EFI_STATUS  Status;
+
+  if (IS_EMPTY_STRING (UnicodeStr)) {
+    return NULL;
+  }
+
+  AsciiStrSize = StrLen (UnicodeStr) + 1;
+  AsciiStr     = AllocateZeroPool (AsciiStrSize);
+  if (AsciiStr == NULL) {
+    return NULL;
+  }
+
+  Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
+    FreePool (AsciiStr);
+    return NULL;
+  }
+
+  return AsciiStr;
+}
+
+/**
+  Return HTTP method in ASCII string. Caller does not need
+  to free returned string buffer.
+
+  @param[in]  Method         HTTP method.
+
+  @retval CHAR8 *   Method in string.
+**/
+CHAR8 *
+HttpMethodToString (
+  IN  EFI_HTTP_METHOD  Method
+  )
+{
+  switch (Method) {
+    case HttpMethodGet:
+      return HTTP_METHOD_GET;
+      break;
+    case HttpMethodPost:
+      return HTTP_METHOD_POST;
+      break;
+    case HttpMethodPatch:
+      return HTTP_METHOD_PATCH;
+      break;
+    case HttpMethodPut:
+      return HTTP_METHOD_PUT;
+      break;
+    case HttpMethodDelete:
+      return HTTP_METHOD_DELETE;
+      break;
+    default:
+      break;
+  }
+
+  return "Unknown";
+}
+
+/**
+  Report HTTP communication error via report status code.
+
+  @param[in]  Method         HTTP method.
+  @param[in]  Uri            The URI which has failure.
+  @param[in]  HttpStatusCode HTTP status code.
+
+**/
+VOID
+ReportHttpError (
+  IN  EFI_HTTP_METHOD       Method,
+  IN  EFI_STRING            Uri,
+  IN  EFI_HTTP_STATUS_CODE  *HttpStatusCode  OPTIONAL
+  )
+{
+  CHAR8  ErrorMsg[REDFISH_ERROR_MSG_MAX];
+
+  if (IS_EMPTY_STRING (Uri)) {
+    DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n", __func__));
+    return;
+  }
+
+  //
+  // Report failure of URI and HTTP status code.
+  //
+  AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT, HttpMethodToString (Method), (HttpStatusCode == NULL ? HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri);
+  DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg));
+  //
+  // TODO:
+  // Below PI status code is approved by PIWG and wait for specification published.
+  // We will uncomment below report status code after PI status code get published.
+  // REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4483
+  //
+  // REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
+  //  EFI_ERROR_CODE | EFI_ERROR_MAJOR,
+  //  EFI_COMPUTING_UNIT_MANAGEABILITY | EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR,
+  //  ErrorMsg,
+  //  AsciiStrSize (ErrorMsg)
+  //  );
+}
+
+/**
+  This function create Redfish service. It's caller's responsibility to free returned
+  Redfish service by calling FreeService ().
+
+  @param[in]  This                       Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  RedfishConfigServiceInfo   Redfish config service information.
+
+  @retval     REDFISH_SERVICE  Redfish service is created.
+  @retval     NULL             Errors occur.
+
+**/
+REDFISH_SERVICE
+EFIAPI
+RedfishCreateRedfishService (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL         *This,
+  IN  REDFISH_CONFIG_SERVICE_INFORMATION  *RedfishConfigServiceInfo
+  )
+{
+  EFI_STATUS                  Status;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+  REDFISH_SERVICE_PRIVATE     *NewService;
+  CHAR8                       *AsciiLocation;
+  CHAR8                       *Host;
+  CHAR8                       *BasicAuthString;
+  UINTN                       BasicAuthStrSize;
+  CHAR8                       *EncodedAuthString;
+  UINTN                       EncodedAuthStrSize;
+  EDKII_REDFISH_AUTH_METHOD   AuthMethod;
+  CHAR8                       *Username;
+  CHAR8                       *Password;
+  UINTN                       UsernameSize;
+  UINTN                       PasswordSize;
+  EFI_REST_EX_PROTOCOL        *RestEx;
+
+  if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) {
+    return NULL;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n", __func__, RedfishConfigServiceInfo->RedfishServiceLocation));
+
+  Private            = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  BasicAuthString    = NULL;
+  EncodedAuthString  = NULL;
+  Username           = NULL;
+  Password           = NULL;
+  NewService         = NULL;
+  AsciiLocation      = NULL;
+  Host               = NULL;
+  BasicAuthStrSize   = 0;
+  EncodedAuthStrSize = 0;
+  UsernameSize       = 0;
+  PasswordSize       = 0;
+
+  //
+  // Build host and host name from service location
+  //
+  if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation)) {
+    AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo->RedfishServiceLocation);
+    if (AsciiLocation == NULL) {
+      goto ON_RELEASE;
+    }
+
+    Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX);
+    if (AsciiLocation == NULL) {
+      goto ON_RELEASE;
+    }
+
+    if (RedfishConfigServiceInfo->RedfishServiceUseHttps) {
+      AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a", AsciiLocation);
+    } else {
+      AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a", AsciiLocation);
+    }
+
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__, Host));
+  }
+
+  //
+  // Find Rest Ex protocol
+  //
+  if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) {
+    Status = gBS->HandleProtocol (
+                    RedfishConfigServiceInfo->RedfishServiceRestExHandle,
+                    &gEfiRestExProtocolGuid,
+                    (VOID **)&RestEx
+                    );
+  } else {
+    DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n", __func__));
+    goto ON_RELEASE;
+  }
+
+  //
+  // Get credential
+  //
+  if (Private->CredentialProtocol == NULL) {
+    //
+    // No credential available on this system.
+    //
+    DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n", __func__));
+  } else {
+    Status = Private->CredentialProtocol->GetAuthInfo (
+                                            Private->CredentialProtocol,
+                                            &AuthMethod,
+                                            &Username,
+                                            &Password
+                                            );
+    if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) || IS_EMPTY_STRING (Password)) {
+      DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information: %r\n", __func__, Status));
+      goto ON_RELEASE;
+    } else {
+      DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x username: %a password: %a\n", __func__, AuthMethod, Username, Password));
+
+      //
+      // Perform base64 encoding (RFC 7617)
+      //
+      UsernameSize     = AsciiStrSize (Username);
+      PasswordSize     = AsciiStrSize (Password);
+      BasicAuthStrSize =  UsernameSize + PasswordSize;  // one byte taken from null-terminator for ':'
+      BasicAuthString  = AllocateZeroPool (BasicAuthStrSize);
+      if (BasicAuthString == NULL) {
+        goto ON_RELEASE;
+      }
+
+      AsciiSPrint (
+        BasicAuthString,
+        BasicAuthStrSize,
+        "%a:%a",
+        Username,
+        Password
+        );
+
+      Status = Base64Encode (
+                 (CONST UINT8 *)BasicAuthString,
+                 BasicAuthStrSize,
+                 EncodedAuthString,
+                 &EncodedAuthStrSize
+                 );
+      if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) {
+        EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize);
+        if (EncodedAuthString == NULL) {
+          goto ON_RELEASE;
+        }
+
+        Status = Base64Encode (
+                   (CONST UINT8 *)BasicAuthString,
+                   BasicAuthStrSize,
+                   EncodedAuthString,
+                   &EncodedAuthStrSize
+                   );
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status));
+        }
+
+        DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization: %a\n", __func__, EncodedAuthString));
+      } else {
+        DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status));
+        goto ON_RELEASE;
+      }
+    }
+  }
+
+  NewService = CreateRedfishService (Host, AsciiLocation, EncodedAuthString, NULL, RestEx);
+  if (NewService == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__));
+  }
+
+ON_RELEASE:
+
+  if (BasicAuthString != NULL) {
+    ZeroMem (BasicAuthString, BasicAuthStrSize);
+    FreePool (BasicAuthString);
+  }
+
+  if (EncodedAuthString != NULL) {
+    ZeroMem (BasicAuthString, EncodedAuthStrSize);
+    FreePool (EncodedAuthString);
+  }
+
+  if (Username != NULL) {
+    ZeroMem (Username, UsernameSize);
+    FreePool (Username);
+  }
+
+  if (Password != NULL) {
+    ZeroMem (Password, PasswordSize);
+    FreePool (Password);
+  }
+
+  if (AsciiLocation != NULL) {
+    FreePool (AsciiLocation);
+  }
+
+  if (Host != NULL) {
+    FreePool (Host);
+  }
+
+  return NewService;
+}
+
+/**
+  This function free resources in Redfish service. RedfishService is no longer available
+  after this function returns successfully.
+
+  @param[in]  This            Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  RedfishService  Pointer to Redfish service to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeRedfishService (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              RedfishService
+  )
+{
+  REDFISH_SERVICE_PRIVATE  *Service;
+
+  if ((This == NULL) || (RedfishService == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Service = (REDFISH_SERVICE_PRIVATE *)RedfishService;
+  if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+  }
+
+  return ReleaseRedfishService (Service);
+}
+
+/**
+  This function returns JSON value in given RedfishPayload. Returned JSON value
+  is a reference to the JSON value in RedfishPayload. Any modification to returned
+  JSON value will change JSON value in RedfishPayload.
+
+  @param[in]  This            Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  RedfishPayload  Pointer to Redfish payload.
+
+  @retval     EDKII_JSON_VALUE   JSON value is returned.
+  @retval     NULL               Errors occur.
+
+**/
+EDKII_JSON_VALUE
+EFIAPI
+RedfishJsonInRedfishPayload (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_PAYLOAD              RedfishPayload
+  )
+{
+  REDFISH_PAYLOAD_PRIVATE  *Payload;
+
+  if ((This == NULL) || (RedfishPayload == NULL)) {
+    return NULL;
+  }
+
+  Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload;
+  if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+  }
+
+  return Payload->JsonValue;
+}
+
+/**
+  Perform HTTP GET to Get redfish resource from given resource URI with
+  cache mechanism supported. It's caller's responsibility to free Response
+  by calling FreeResponse ().
+
+  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Service       Redfish service instance to perform HTTP GET.
+  @param[in]  Uri           Target resource URI.
+  @param[in]  Request       Additional request context. This is optional.
+  @param[out] Response      HTTP response from redfish service.
+  @param[in]  UseCache      If it is TRUE, this function will search for
+                            cache first. If it is FALSE, this function
+                            will query Redfish URI directly.
+
+  @retval     EFI_SUCCESS     Resrouce is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishGetResource (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              Service,
+  IN  EFI_STRING                   Uri,
+  IN  REDFISH_REQUEST              *Request OPTIONAL,
+  OUT REDFISH_RESPONSE             *Response,
+  IN  BOOLEAN                      UseCache
+  )
+{
+  EFI_STATUS                  Status;
+  REDFISH_HTTP_CACHE_DATA     *CacheData;
+  UINTN                       RetryCount;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n", __func__, Uri, (UseCache ? "true" : "false")));
+
+  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  CacheData  = NULL;
+  RetryCount = 0;
+  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+
+  if (Private->CacheDisabled) {
+    UseCache = FALSE;
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by PCD!\n", __func__));
+  }
+
+  //
+  // Search for cache list.
+  //
+  if (UseCache) {
+    CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
+    if (CacheData != NULL) {
+      DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit! %s\n", __func__, Uri));
+
+      //
+      // Copy cached response to caller's buffer.
+      //
+      Status               = CopyRedfishResponse (CacheData->Response, Response);
+      CacheData->HitCount += 1;
+      return Status;
+    }
+  }
+
+  //
+  // Get resource from redfish service.
+  //
+  do {
+    RetryCount += 1;
+    Status      = HttpSendReceive (
+                    Service,
+                    Uri,
+                    HttpMethodGet,
+                    Request,
+                    Response
+                    );
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+    if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryGet)) {
+      break;
+    }
+
+    //
+    // Retry when BMC is not ready.
+    //
+    if ((Response->StatusCode != NULL)) {
+      DEBUG_CODE (
+        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+        );
+
+      if (!RedfishRetryRequired (Response->StatusCode)) {
+        break;
+      }
+
+      //
+      // Release response for next round of request.
+      //
+      This->FreeResponse (This, Response);
+    }
+
+    DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryGet));
+    if (Private->RetrySetting.RetryWait > 0) {
+      gBS->Stall (Private->RetrySetting.RetryWait);
+    }
+  } while (TRUE);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG_CODE (
+      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+      );
+    //
+    // Report status code for Redfish failure
+    //
+    ReportHttpError (HttpMethodGet, Uri, Response->StatusCode);
+    DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryGet, Status));
+    goto ON_RELEASE;
+  }
+
+  if (!Private->CacheDisabled) {
+    //
+    // Keep response in cache list
+    //
+    Status = AddHttpCacheData (&Private->CacheList, Uri, Response);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri, Status));
+      goto ON_RELEASE;
+    }
+
+    DEBUG_CODE (
+      DebugPrintHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList);
+      );
+  }
+
+ON_RELEASE:
+
+  return Status;
+}
+
+/**
+  This function free resources in Request. Request is no longer available
+  after this function returns successfully.
+
+  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Request      HTTP request to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeRequest (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_REQUEST              *Request
+  )
+{
+  if ((This == NULL) || (Request == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
+
+  return ReleaseRedfishRequest (Request);
+}
+
+/**
+  This function free resources in given Response.
+
+  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Response     HTTP response to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeResponse (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_RESPONSE             *Response
+  )
+{
+  if ((This == NULL) || (Response == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
+
+  return ReleaseRedfishResponse (Response);
+}
+
+/**
+  This function expire the cached response of given URI.
+
+  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Uri          Target response of URI.
+
+  @retval     EFI_SUCCESS     Target response is expired successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishExpireResponse (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  EFI_STRING                   Uri
+  )
+{
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+  REDFISH_HTTP_CACHE_DATA     *CacheData;
+
+  if ((This == NULL) || IS_EMPTY_STRING (Uri)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__, Uri));
+
+  Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+
+  CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
+  if (CacheData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  return DeleteHttpCacheData (&Private->CacheList, CacheData);
+}
+
+/**
+  Perform HTTP PATCH to send redfish resource to given resource URI.
+  It's caller's responsibility to free Response by calling FreeResponse ().
+
+  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Service       Redfish service instance to perform HTTP PATCH.
+  @param[in]  Uri           Target resource URI.
+  @param[in]  Content       Data to patch.
+  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
+                            This is optional. When ContentSize is 0, ContentSize
+                            is the size of Content.
+  @param[in]  ContentType   Type of the Content to be send to Redfish service.
+                            This is optional. When ContentType is NULL, content
+                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
+  @param[out] Response      HTTP response from redfish service.
+
+  @retval     EFI_SUCCESS     Resrouce is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPatchResource (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              Service,
+  IN  EFI_STRING                   Uri,
+  IN  CHAR8                        *Content,
+  IN  UINTN                        ContentSize OPTIONAL,
+  IN  CHAR8                        *ContentType OPTIONAL,
+  OUT REDFISH_RESPONSE             *Response
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       RetryCount;
+  REDFISH_REQUEST             Request;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__, Uri));
+
+  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  RetryCount = 0;
+  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+  Request.Content       = Content;
+  Request.ContentLength = ContentSize;
+  Request.ContentType   = ContentType;
+
+  //
+  // Patch resource to redfish service.
+  //
+  do {
+    RetryCount += 1;
+    Status      = HttpSendReceive (
+                    Service,
+                    Uri,
+                    HttpMethodPatch,
+                    &Request,
+                    Response
+                    );
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+    if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPatch)) {
+      break;
+    }
+
+    //
+    // Retry when BMC is not ready.
+    //
+    if ((Response->StatusCode != NULL)) {
+      DEBUG_CODE (
+        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+        );
+
+      if (!RedfishRetryRequired (Response->StatusCode)) {
+        break;
+      }
+
+      //
+      // Release response for next round of request.
+      //
+      This->FreeResponse (This, Response);
+    }
+
+    DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPatch));
+    if (Private->RetrySetting.RetryWait > 0) {
+      gBS->Stall (Private->RetrySetting.RetryWait);
+    }
+  } while (TRUE);
+
+  //
+  // Redfish resource is updated. Automatically expire the cached response
+  // so application can directly get resource from Redfish service again.
+  //
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+  RedfishExpireResponse (This, Uri);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG_CODE (
+      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+      );
+    //
+    // Report status code for Redfish failure
+    //
+    ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode);
+    DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status));
+    goto ON_RELEASE;
+  }
+
+ON_RELEASE:
+
+  return Status;
+}
+
+/**
+  Perform HTTP PUT to send redfish resource to given resource URI.
+  It's caller's responsibility to free Response by calling FreeResponse ().
+
+  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Service       Redfish service instance to perform HTTP PUT.
+  @param[in]  Uri           Target resource URI.
+  @param[in]  Content       Data to put.
+  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
+                            This is optional. When ContentSize is 0, ContentSize
+                            is the size of Content.
+  @param[in]  ContentType   Type of the Content to be send to Redfish service.
+                            This is optional. When ContentType is NULL, content
+                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
+  @param[out] Response      HTTP response from redfish service.
+
+  @retval     EFI_SUCCESS     Resrouce is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPutResource (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              Service,
+  IN  EFI_STRING                   Uri,
+  IN  CHAR8                        *Content,
+  IN  UINTN                        ContentSize OPTIONAL,
+  IN  CHAR8                        *ContentType OPTIONAL,
+  OUT REDFISH_RESPONSE             *Response
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       RetryCount;
+  REDFISH_REQUEST             Request;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__, Uri));
+
+  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  RetryCount = 0;
+  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+  Request.Content       = Content;
+  Request.ContentLength = ContentSize;
+  Request.ContentType   = ContentType;
+
+  //
+  // Patch resource to redfish service.
+  //
+  do {
+    RetryCount += 1;
+    Status      = HttpSendReceive (
+                    Service,
+                    Uri,
+                    HttpMethodPut,
+                    &Request,
+                    Response
+                    );
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+    if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPut)) {
+      break;
+    }
+
+    //
+    // Retry when BMC is not ready.
+    //
+    if ((Response->StatusCode != NULL)) {
+      DEBUG_CODE (
+        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+        );
+
+      if (!RedfishRetryRequired (Response->StatusCode)) {
+        break;
+      }
+
+      //
+      // Release response for next round of request.
+      //
+      This->FreeResponse (This, Response);
+    }
+
+    DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPut));
+    if (Private->RetrySetting.RetryWait > 0) {
+      gBS->Stall (Private->RetrySetting.RetryWait);
+    }
+  } while (TRUE);
+
+  //
+  // Redfish resource is updated. Automatically expire the cached response
+  // so application can directly get resource from Redfish service again.
+  //
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+  RedfishExpireResponse (This, Uri);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG_CODE (
+      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+      );
+    //
+    // Report status code for Redfish failure
+    //
+    ReportHttpError (HttpMethodPut, Uri, Response->StatusCode);
+    DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status));
+    goto ON_RELEASE;
+  }
+
+ON_RELEASE:
+
+  return Status;
+}
+
+/**
+  Perform HTTP POST to send redfish resource to given resource URI.
+  It's caller's responsibility to free Response by calling FreeResponse ().
+
+  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Service       Redfish service instance to perform HTTP POST.
+  @param[in]  Uri           Target resource URI.
+  @param[in]  Content       Data to post.
+  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
+                            This is optional. When ContentSize is 0, ContentSize
+                            is the size of Content.
+  @param[in]  ContentType   Type of the Content to be send to Redfish service.
+                            This is optional. When ContentType is NULL, content
+                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
+  @param[out] Response      HTTP response from redfish service.
+
+  @retval     EFI_SUCCESS     Resrouce is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPostResource (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              Service,
+  IN  EFI_STRING                   Uri,
+  IN  CHAR8                        *Content,
+  IN  UINTN                        ContentSize OPTIONAL,
+  IN  CHAR8                        *ContentType OPTIONAL,
+  OUT REDFISH_RESPONSE             *Response
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       RetryCount;
+  REDFISH_REQUEST             Request;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__, Uri));
+
+  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  RetryCount = 0;
+  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+  Request.Content       = Content;
+  Request.ContentLength = ContentSize;
+  Request.ContentType   = ContentType;
+
+  //
+  // Patch resource to redfish service.
+  //
+  do {
+    RetryCount += 1;
+    Status      = HttpSendReceive (
+                    Service,
+                    Uri,
+                    HttpMethodPost,
+                    &Request,
+                    Response
+                    );
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+    if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPost)) {
+      break;
+    }
+
+    //
+    // Retry when BMC is not ready.
+    //
+    if ((Response->StatusCode != NULL)) {
+      DEBUG_CODE (
+        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+        );
+
+      if (!RedfishRetryRequired (Response->StatusCode)) {
+        break;
+      }
+
+      //
+      // Release response for next round of request.
+      //
+      This->FreeResponse (This, Response);
+    }
+
+    DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPost));
+    if (Private->RetrySetting.RetryWait > 0) {
+      gBS->Stall (Private->RetrySetting.RetryWait);
+    }
+  } while (TRUE);
+
+  //
+  // Redfish resource is updated. Automatically expire the cached response
+  // so application can directly get resource from Redfish service again.
+  //
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+  RedfishExpireResponse (This, Uri);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG_CODE (
+      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+      );
+    //
+    // Report status code for Redfish failure
+    //
+    ReportHttpError (HttpMethodPost, Uri, Response->StatusCode);
+    DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status));
+    goto ON_RELEASE;
+  }
+
+ON_RELEASE:
+
+  return Status;
+}
+
+/**
+  Perform HTTP DELETE to delete redfish resource on given resource URI.
+  It's caller's responsibility to free Response by calling FreeResponse ().
+
+  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+  @param[in]  Service       Redfish service instance to perform HTTP DELETE.
+  @param[in]  Uri           Target resource URI.
+  @param[in]  Content       JSON represented properties to be deleted. This is
+                            optional.
+  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
+                            This is optional. When ContentSize is 0, ContentSize
+                            is the size of Content if Content is not NULL.
+  @param[in]  ContentType   Type of the Content to be send to Redfish service.
+                            This is optional. When Content is not NULL and
+                            ContentType is NULL, content type HTTP_CONTENT_TYPE_APP_JSON
+                            will be used.
+  @param[out] Response      HTTP response from redfish service.
+
+  @retval     EFI_SUCCESS     Resrouce is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishDeleteResource (
+  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
+  IN  REDFISH_SERVICE              Service,
+  IN  EFI_STRING                   Uri,
+  IN  CHAR8                        *Content OPTIONAL,
+  IN  UINTN                        ContentSize OPTIONAL,
+  IN  CHAR8                        *ContentType OPTIONAL,
+  OUT REDFISH_RESPONSE             *Response
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       RetryCount;
+  REDFISH_REQUEST             Request;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__, Uri));
+
+  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+  RetryCount = 0;
+  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+  Request.Content       = Content;
+  Request.ContentLength = ContentSize;
+  Request.ContentType   = ContentType;
+
+  //
+  // Patch resource to redfish service.
+  //
+  do {
+    RetryCount += 1;
+    Status      = HttpSendReceive (
+                    Service,
+                    Uri,
+                    HttpMethodDelete,
+                    &Request,
+                    Response
+                    );
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+    if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryDelete)) {
+      break;
+    }
+
+    //
+    // Retry when BMC is not ready.
+    //
+    if ((Response->StatusCode != NULL)) {
+      DEBUG_CODE (
+        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+        );
+
+      if (!RedfishRetryRequired (Response->StatusCode)) {
+        break;
+      }
+
+      //
+      // Release response for next round of request.
+      //
+      This->FreeResponse (This, Response);
+    }
+
+    DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryDelete));
+    if (Private->RetrySetting.RetryWait > 0) {
+      gBS->Stall (Private->RetrySetting.RetryWait);
+    }
+  } while (TRUE);
+
+  //
+  // Redfish resource is updated. Automatically expire the cached response
+  // so application can directly get resource from Redfish service again.
+  //
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+  RedfishExpireResponse (This, Uri);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG_CODE (
+      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+      );
+    //
+    // Report status code for Redfish failure
+    //
+    ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode);
+    DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status));
+    goto ON_RELEASE;
+  }
+
+ON_RELEASE:
+
+  return Status;
+}
+
+EDKII_REDFISH_HTTP_PROTOCOL  mEdkIIRedfishHttpProtocol = {
+  EDKII_REDFISH_HTTP_PROTOCOL_REVISION,
+  RedfishCreateRedfishService,
+  RedfishFreeRedfishService,
+  RedfishJsonInRedfishPayload,
+  RedfishGetResource,
+  RedfishPatchResource,
+  RedfishPutResource,
+  RedfishPostResource,
+  RedfishDeleteResource,
+  RedfishFreeRequest,
+  RedfishFreeResponse,
+  RedfishExpireResponse
+};
+
+/**
+  Unloads an image.
+
+  @param[in]  ImageHandle         Handle that identifies the image to be unloaded.
+
+  @retval EFI_SUCCESS             The image has been unloaded.
+  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpDriverUnload (
+  IN EFI_HANDLE  ImageHandle
+  )
+{
+  if (mRedfishHttpCachePrivate == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
+    ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
+  }
+
+  gBS->UninstallMultipleProtocolInterfaces (
+         ImageHandle,
+         &gEdkIIRedfishHttpProtocolGuid,
+         &mRedfishHttpCachePrivate->Protocol,
+         NULL
+         );
+
+  FreePool (mRedfishHttpCachePrivate);
+  mRedfishHttpCachePrivate = NULL;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler.
+
+  @param[in] Event    Event whose notification function is being invoked.
+  @param[in] Context  Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+CredentialProtocolInstalled (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  EFI_STATUS                  Status;
+  REDFISH_HTTP_CACHE_PRIVATE  *Private;
+
+  Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context;
+  if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+    return;
+  }
+
+  //
+  // Locate HII database protocol.
+  //
+  Status = gBS->LocateProtocol (
+                  &gEdkIIRedfishCredentialProtocolGuid,
+                  NULL,
+                  (VOID **)&Private->CredentialProtocol
+                  );
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  gBS->CloseEvent (Event);
+}
+
+/**
+  Main entry for this driver.
+
+  @param[in] ImageHandle     Image handle this driver.
+  @param[in] SystemTable     Pointer to SystemTable.
+
+  @retval EFI_SUCCESS     This function always complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  VOID        *Registration;
+
+  if (mRedfishHttpCachePrivate != NULL) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  mRedfishHttpCachePrivate = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_PRIVATE));
+  if (mRedfishHttpCachePrivate == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Initial cache list and protocol instance.
+  //
+  mRedfishHttpCachePrivate->Signature   = REDFISH_HTTP_DRIVER_SIGNATURE;
+  mRedfishHttpCachePrivate->ImageHandle = ImageHandle;
+  CopyMem (&mRedfishHttpCachePrivate->Protocol, &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL));
+  mRedfishHttpCachePrivate->CacheList.Capacity = REDFISH_HTTP_CACHE_LIST_SIZE;
+  mRedfishHttpCachePrivate->CacheList.Count    = 0x00;
+  mRedfishHttpCachePrivate->CacheDisabled      = PcdGetBool (PcdHttpCacheDisabled);
+  InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
+
+  //
+  // Get retry settings
+  //
+  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet    = PcdGet16 (PcdHttpGetRetry);
+  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut    = PcdGet16 (PcdHttpPutRetry);
+  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch  = PcdGet16 (PcdHttpPatchRetry);
+  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost   = PcdGet16 (PcdHttpPostRetry);
+  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16 (PcdHttpDeleteRetry);
+  mRedfishHttpCachePrivate->RetrySetting.RetryWait          = PcdGet16 (PcdHttpRetryWaitInSecond) * 1000000U;
+
+  //
+  // Install the gEdkIIRedfishHttpProtocolGuid onto Handle.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mRedfishHttpCachePrivate->ImageHandle,
+                  &gEdkIIRedfishHttpProtocolGuid,
+                  &mRedfishHttpCachePrivate->Protocol,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n", __func__, Status));
+    RedfishHttpDriverUnload (ImageHandle);
+    return Status;
+  }
+
+  //
+  // Install protocol notification if credential protocol is installed.
+  //
+  mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent (
+                                            &gEdkIIRedfishCredentialProtocolGuid,
+                                            TPL_CALLBACK,
+                                            CredentialProtocolInstalled,
+                                            mRedfishHttpCachePrivate,
+                                            &Registration
+                                            );
+  if (mRedfishHttpCachePrivate->NotifyEvent == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for gEdkIIRedfishCredentialProtocolGuid\n", __func__));
+    ASSERT (FALSE);
+    RedfishHttpDriverUnload (ImageHandle);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
new file mode 100644
index 0000000000..5652818d16
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
@@ -0,0 +1,693 @@
+/** @file
+  RedfishHttpOperation handles HTTP operations.
+
+  Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpOperation.h"
+#include "RedfishHttpData.h"
+
+/**
+  This function copies all headers in SrcHeaders to DstHeaders.
+  It's call responsibility to release returned DstHeaders.
+
+  @param[in]  SrcHeaders      Source headers.
+  @param[in]  SrcHeaderCount  Number of header in source headers.
+  @param[out] DstHeaders      Destination headers.
+  @param[out] DstHeaderCount  Number of header in designation headers.
+
+  @retval     EFI_SUCCESS     Headers are copied successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+CopyHttpHeaders (
+  IN  EFI_HTTP_HEADER  *SrcHeaders,
+  IN  UINTN            SrcHeaderCount,
+  OUT EFI_HTTP_HEADER  **DstHeaders,
+  OUT UINTN            *DstHeaderCount
+  )
+{
+  UINTN  Index;
+
+  if ((SrcHeaders == NULL) || (SrcHeaderCount == 0) || (DstHeaders == NULL) || (DstHeaderCount == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *DstHeaderCount = 0;
+  *DstHeaders     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcHeaderCount);
+  if (*DstHeaders == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  for (Index = 0; Index < SrcHeaderCount; Index++) {
+    (*DstHeaders)[Index].FieldName = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldName), SrcHeaders[Index].FieldName);
+    if ((*DstHeaders)[Index].FieldName == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
+    if ((*DstHeaders)[Index].FieldValue == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    *DstHeaderCount += 1;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function free resources in Request. Request is no longer available
+  after this function returns successfully.
+
+  @param[in]  Request      HTTP request to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishRequest (
+  IN  REDFISH_REQUEST  *Request
+  )
+{
+  if (Request == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Request->Headers != NULL) && (Request->HeaderCount > 0)) {
+    HttpFreeHeaderFields (Request->Headers, Request->HeaderCount);
+    Request->Headers     = NULL;
+    Request->HeaderCount = 0;
+  }
+
+  if (Request->Content != NULL) {
+    FreePool (Request->Content);
+    Request->Content = NULL;
+  }
+
+  if (Request->ContentType != NULL) {
+    FreePool (Request->ContentType);
+    Request->ContentType = NULL;
+  }
+
+  Request->ContentLength = 0;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function free resources in given Response.
+
+  @param[in]  Response     HTTP response to be released.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishResponse (
+  IN  REDFISH_RESPONSE  *Response
+  )
+{
+  if (Response == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Response->Headers != NULL) && (Response->HeaderCount > 0)) {
+    HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
+    Response->Headers     = NULL;
+    Response->HeaderCount = 0;
+  }
+
+  if (Response->Payload != NULL) {
+    ReleaseRedfishPayload (Response->Payload);
+    Response->Payload = NULL;
+  }
+
+  if (Response->StatusCode != NULL) {
+    FreePool (Response->StatusCode);
+    Response->StatusCode = NULL;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function free resources in given HTTP message.
+
+  @param[in]  HttpMessage     HTTP message to be released.
+  @param[in]  IsRequest       TRUE if this is request type of HTTP message.
+                              FALSE if this is response type of HTTP message.
+
+  @retval     EFI_SUCCESS     Resrouce is released successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ReleaseHttpMessage (
+  IN  EFI_HTTP_MESSAGE  *HttpMessage,
+  IN  BOOLEAN           IsRequest
+  )
+{
+  if (HttpMessage == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IsRequest) {
+    if (HttpMessage->Data.Request != NULL) {
+      if (HttpMessage->Data.Request->Url != NULL) {
+        FreePool (HttpMessage->Data.Request->Url);
+      }
+
+      FreePool (HttpMessage->Data.Request);
+      HttpMessage->Data.Request = NULL;
+    }
+  } else {
+    if (HttpMessage->Data.Response != NULL) {
+      FreePool (HttpMessage->Data.Response);
+      HttpMessage->Data.Response = NULL;
+    }
+  }
+
+  if (HttpMessage->Body != NULL) {
+    FreePool (HttpMessage->Body);
+    HttpMessage->Body = NULL;
+  }
+
+  if (HttpMessage->Headers != NULL) {
+    HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage->HeaderCount);
+    HttpMessage->Headers     = NULL;
+    HttpMessage->HeaderCount = 0;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function build Redfish message for sending data to Redfish service.
+  It's call responsibility to properly release returned HTTP message by
+  calling ReleaseHttpMessage.
+
+  @param[in]   ServicePrivate    Pointer to Redfish service private data.
+  @param[in]   Uri               Redfish service URI.
+  @param[in]   Method            HTTP method.
+  @param[in]   Request           Additional data to send to Redfish service.
+                                 This is optional.
+  @param[in]   ContentEncoding   Content encoding method to compress HTTP context.
+                                 This is optional. When ContentEncoding is NULL,
+                                 No compress method will be performed.
+
+  @retval     EFI_HTTP_MESSAGE *   Pointer to newly created HTTP message.
+  @retval     NULL                 Error occurred.
+
+**/
+EFI_HTTP_MESSAGE *
+BuildRequestMessage (
+  IN REDFISH_SERVICE_PRIVATE  *ServicePrivate,
+  IN EFI_STRING               Uri,
+  IN EFI_HTTP_METHOD          Method,
+  IN REDFISH_REQUEST          *Request OPTIONAL,
+  IN CHAR8                    *ContentEncoding OPTIONAL
+  )
+{
+  EFI_STATUS             Status;
+  EFI_STRING             Url;
+  UINTN                  UrlSize;
+  UINTN                  Index;
+  EFI_HTTP_MESSAGE       *RequestMsg;
+  EFI_HTTP_REQUEST_DATA  *RequestData;
+  UINTN                  HeaderCount;
+  UINTN                  HeaderIndex;
+  EFI_HTTP_HEADER        *Headers;
+  CHAR8                  ContentLengthStr[REDFISH_CONTENT_LENGTH_SIZE];
+  VOID                   *Content;
+  UINTN                  ContentLength;
+  BOOLEAN                HasContent;
+  BOOLEAN                DoContentEncoding;
+
+  RequestMsg        = NULL;
+  RequestData       = NULL;
+  Url               = NULL;
+  UrlSize           = 0;
+  Content           = NULL;
+  ContentLength     = 0;
+  HeaderCount       = REDFISH_COMMON_HEADER_SIZE;
+  HeaderIndex       = 0;
+  Headers           = NULL;
+  HasContent        = FALSE;
+  DoContentEncoding = FALSE;
+
+  if ((ServicePrivate == NULL) || (IS_EMPTY_STRING (Uri))) {
+    return NULL;
+  }
+
+  if (Method >= HttpMethodMax) {
+    return NULL;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__, Uri));
+
+  //
+  // Build full URL for HTTP query.
+  //
+  UrlSize = (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * sizeof (CHAR16);
+  Url     = AllocateZeroPool (UrlSize);
+  if (Url == NULL) {
+    return NULL;
+  }
+
+  UnicodeSPrint (Url, UrlSize, L"%a%s", ServicePrivate->Host, Uri);
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Url: %s\n", __func__, Url));
+
+  //
+  // Step 1: build the HTTP headers.
+  //
+  if (!IS_EMPTY_STRING (ServicePrivate->SessionToken) || !IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
+    HeaderCount++;
+  }
+
+  if ((Request != NULL) && (Request->HeaderCount > 0)) {
+    HeaderCount += Request->HeaderCount;
+  }
+
+  //
+  // Check and see if we will do content encoding or not
+  //
+  if (!IS_EMPTY_STRING (ContentEncoding)) {
+    if (AsciiStrCmp (ContentEncoding, REDFISH_HTTP_CONTENT_ENCODING_NONE) != 0) {
+      DoContentEncoding = TRUE;
+    }
+  }
+
+  if ((Request != NULL) && !IS_EMPTY_STRING (Request->Content)) {
+    HeaderCount += 2;
+    HasContent   = TRUE;
+    if (DoContentEncoding) {
+      HeaderCount += 1;
+    }
+  }
+
+  Headers = AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER));
+  if (Headers == NULL) {
+    goto ON_ERROR;
+  }
+
+  if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) {
+    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_X_AUTH_TOKEN, ServicePrivate->SessionToken);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  } else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
+    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_AUTHORIZATION, ServicePrivate->BasicAuth);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  }
+
+  if (Request != NULL) {
+    for (Index = 0; Index < Request->HeaderCount; Index++) {
+      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], Request->Headers[Index].FieldName, Request->Headers[Index].FieldValue);
+      if (EFI_ERROR (Status)) {
+        goto ON_ERROR;
+      }
+    }
+  }
+
+  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_HOST, ServicePrivate->HostName);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_ODATA_VERSION_STR, REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_USER_AGENT, REDFISH_HTTP_HEADER_USER_AGENT_VALUE);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_CONNECTION_STR, REDFISH_HTTP_HEADER_CONNECTION_VALUE);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Handle content header
+  //
+  if (HasContent) {
+    if (Request->ContentType == NULL) {
+      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON);
+      if (EFI_ERROR (Status)) {
+        goto ON_ERROR;
+      }
+    } else {
+      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, Request->ContentType);
+      if (EFI_ERROR (Status)) {
+        goto ON_ERROR;
+      }
+    }
+
+    if (Request->ContentLength == 0) {
+      Request->ContentLength =  AsciiStrLen (Request->Content);
+    }
+
+    AsciiSPrint (
+      ContentLengthStr,
+      sizeof (ContentLengthStr),
+      "%lu",
+      (UINT64)Request->ContentLength
+      );
+    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_LENGTH, ContentLengthStr);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Encoding
+    //
+    if (DoContentEncoding) {
+      //
+      // We currently only support gzip Content-Encoding.
+      //
+      Status =  RedfishContentEncode (
+                  ContentEncoding,
+                  Request->Content,
+                  Request->ContentLength,
+                  &Content,
+                  &ContentLength
+                  );
+      if (Status == EFI_INVALID_PARAMETER) {
+        DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__));
+        goto ON_ERROR;
+      } else if (Status == EFI_UNSUPPORTED) {
+        DoContentEncoding = FALSE;
+        DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content coding for %a! Use raw data instead.\n", __func__, ContentEncoding));
+        Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_IDENTITY);
+        if (EFI_ERROR (Status)) {
+          goto ON_ERROR;
+        }
+      } else {
+        Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_GZIP);
+        if (EFI_ERROR (Status)) {
+          goto ON_ERROR;
+        }
+      }
+    }
+
+    //
+    // When the content is from caller, we use our own copy so that we properly release it later.
+    //
+    if (!DoContentEncoding) {
+      Content = AllocateCopyPool (Request->ContentLength, Request->Content);
+      if (Content == NULL) {
+        goto ON_ERROR;
+      }
+
+      ContentLength = Request->ContentLength;
+    }
+  }
+
+  //
+  // Step 2: build the rest of HTTP request info.
+  //
+  RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA));
+  if (RequestData == NULL) {
+    goto ON_ERROR;
+  }
+
+  RequestData->Method = Method;
+  RequestData->Url    = Url;
+
+  //
+  // Step 3: fill in EFI_HTTP_MESSAGE
+  //
+  RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE));
+  if (RequestMsg == NULL) {
+    goto ON_ERROR;
+  }
+
+  ASSERT (HeaderIndex == HeaderCount);
+  RequestMsg->Data.Request = RequestData;
+  RequestMsg->HeaderCount  = HeaderIndex;
+  RequestMsg->Headers      = Headers;
+
+  if (HasContent) {
+    RequestMsg->BodyLength = ContentLength;
+    RequestMsg->Body       = Content;
+  }
+
+  return RequestMsg;
+
+ON_ERROR:
+
+  if (Headers != NULL) {
+    HttpFreeHeaderFields (Headers, HeaderIndex);
+  }
+
+  if (RequestData != NULL) {
+    FreePool (RequestData);
+  }
+
+  if (RequestMsg != NULL) {
+    FreePool (RequestMsg);
+  }
+
+  if (Url != NULL) {
+    FreePool (Url);
+  }
+
+  return NULL;
+}
+
+/**
+  This function parse response message from Redfish service, and
+  build Redfish response for caller. It's call responsibility to
+  properly release Redfish response by calling ReleaseRedfishResponse.
+
+  @param[in]   ServicePrivate   Pointer to Redfish service private data.
+  @param[in]   ResponseMsg      Response message from Redfish service.
+  @param[out]  RedfishResponse  Redfish response data.
+
+  @retval     EFI_SUCCESS     Redfish response is returned successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+ParseResponseMessage (
+  IN  REDFISH_SERVICE_PRIVATE  *ServicePrivate,
+  IN  EFI_HTTP_MESSAGE         *ResponseMsg,
+  OUT REDFISH_RESPONSE         *RedfishResponse
+  )
+{
+  EFI_STATUS        Status;
+  EDKII_JSON_VALUE  JsonData;
+  EFI_HTTP_HEADER   *ContentEncodedHeader;
+  VOID              *DecodedBody;
+  UINTN             DecodedLength;
+
+  if ((ServicePrivate == NULL) || (ResponseMsg == NULL) || (RedfishResponse == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__));
+
+  //
+  // Initialization
+  //
+  JsonData                     = NULL;
+  RedfishResponse->HeaderCount = 0;
+  RedfishResponse->Headers     = NULL;
+  RedfishResponse->Payload     = NULL;
+  RedfishResponse->StatusCode  = NULL;
+  DecodedBody                  = NULL;
+  DecodedLength                = 0;
+
+  //
+  // Return the HTTP StatusCode.
+  //
+  if (ResponseMsg->Data.Response != NULL) {
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n", __func__, ResponseMsg->Data.Response->StatusCode));
+    RedfishResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), &ResponseMsg->Data.Response->StatusCode);
+    if (RedfishResponse->StatusCode == NULL) {
+      DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n", __func__));
+    }
+  }
+
+  //
+  // Return the HTTP headers.
+  //
+  if (ResponseMsg->Headers != NULL) {
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count: %d\n", __func__, ResponseMsg->HeaderCount));
+    Status = CopyHttpHeaders (
+               ResponseMsg->Headers,
+               ResponseMsg->HeaderCount,
+               &RedfishResponse->Headers,
+               &RedfishResponse->HeaderCount
+               );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: Failed to copy HTTP headers: %r\n", __func__, Status));
+    }
+  }
+
+  //
+  // Return the HTTP body.
+  //
+  if ((ResponseMsg->BodyLength != 0) && (ResponseMsg->Body != NULL)) {
+    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length: %d\n", __func__, ResponseMsg->BodyLength));
+    //
+    // Check if data is encoded.
+    //
+    ContentEncodedHeader = HttpFindHeader (RedfishResponse->HeaderCount, RedfishResponse->Headers, HTTP_HEADER_CONTENT_ENCODING);
+    if (ContentEncodedHeader != NULL) {
+      //
+      // The content is encoded.
+      //
+      Status = RedfishContentDecode (
+                 ContentEncodedHeader->FieldValue,
+                 ResponseMsg->Body,
+                 ResponseMsg->BodyLength,
+                 &DecodedBody,
+                 &DecodedLength
+                 );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response content: %r decoding method: %a\n.", __func__, Status, ContentEncodedHeader->FieldValue));
+        goto ON_ERROR;
+      }
+
+      JsonData = JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL);
+      FreePool (DecodedBody);
+    } else {
+      JsonData = JsonLoadBuffer (ResponseMsg->Body, ResponseMsg->BodyLength, 0, NULL);
+    }
+
+    if (!JsonValueIsNull (JsonData)) {
+      RedfishResponse->Payload = CreateRedfishPayload (ServicePrivate, JsonData);
+      if (RedfishResponse->Payload == NULL) {
+        DEBUG ((DEBUG_ERROR, "%a: Failed to create payload\n.", __func__));
+      }
+
+      JsonValueFree (JsonData);
+    } else {
+      DEBUG ((DEBUG_ERROR, "%a: No payload available\n", __func__));
+    }
+  }
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+
+  if (RedfishResponse != NULL) {
+    ReleaseRedfishResponse (RedfishResponse);
+  }
+
+  return Status;
+}
+
+/**
+  This function send Redfish request to Redfish service by calling
+  Rest Ex protocol.
+
+  @param[in]   Service       Pointer to Redfish service.
+  @param[in]   Uri           Uri of Redfish service.
+  @param[in]   Method        HTTP method.
+  @param[in]   Request     Request data. This is optional.
+  @param[out]  Response    Redfish response data.
+
+  @retval     EFI_SUCCESS     Request is sent and received successfully.
+  @retval     Others          Errors occur.
+
+**/
+EFI_STATUS
+HttpSendReceive (
+  IN  REDFISH_SERVICE   Service,
+  IN  EFI_STRING        Uri,
+  IN  EFI_HTTP_METHOD   Method,
+  IN  REDFISH_REQUEST   *Request  OPTIONAL,
+  OUT REDFISH_RESPONSE  *Response
+  )
+{
+  EFI_STATUS               Status;
+  EFI_STATUS               RestExStatus;
+  EFI_HTTP_MESSAGE         *RequestMsg;
+  EFI_HTTP_MESSAGE         ResponseMsg;
+  REDFISH_SERVICE_PRIVATE  *ServicePrivate;
+  EFI_HTTP_HEADER          *XAuthTokenHeader;
+  CHAR8                    *HttpContentEncoding;
+
+  if ((Service == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x %s\n", __func__, Method, Uri));
+
+  ServicePrivate = (REDFISH_SERVICE_PRIVATE *)Service;
+  if (ServicePrivate->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (&ResponseMsg, sizeof (ResponseMsg));
+  HttpContentEncoding = (CHAR8 *)PcdGetPtr (PcdRedfishServiceContentEncoding);
+
+  RequestMsg = BuildRequestMessage (Service, Uri, Method, Request, HttpContentEncoding);
+  if (RequestMsg == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n", __func__, Uri));
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // call RESTEx to get response from REST service.
+  //
+  RestExStatus = ServicePrivate->RestEx->SendReceive (ServicePrivate->RestEx, RequestMsg, &ResponseMsg);
+  if (EFI_ERROR (RestExStatus)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s SendReceive failure: %r\n", __func__, Uri, RestExStatus));
+  }
+
+  //
+  // Return status code, headers and payload to caller as much as possible even when RestEx returns failure.
+  //
+  Status = ParseResponseMessage (ServicePrivate, &ResponseMsg, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s parse response failure: %r\n", __func__, Uri, Status));
+  } else {
+    //
+    // Capture session token in header
+    //
+    if ((Method == HttpMethodPost) &&
+        (Response->StatusCode != NULL) &&
+        ((*Response->StatusCode == HTTP_STATUS_200_OK) || (*Response->StatusCode == HTTP_STATUS_204_NO_CONTENT)))
+    {
+      XAuthTokenHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN);
+      if (XAuthTokenHeader != NULL) {
+        Status = UpdateSessionToken (ServicePrivate, XAuthTokenHeader->FieldValue);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n", __func__, Status));
+        }
+      }
+    }
+  }
+
+  //
+  // Release resources
+  //
+  if (RequestMsg != NULL) {
+    ReleaseHttpMessage (RequestMsg, TRUE);
+    FreePool (RequestMsg);
+  }
+
+  ReleaseHttpMessage (&ResponseMsg, FALSE);
+
+  return RestExStatus;
+}
diff --git a/RedfishPkg/Redfish.fdf.inc b/RedfishPkg/Redfish.fdf.inc
index 3e5a77766e..5cbe3592fd 100644
--- a/RedfishPkg/Redfish.fdf.inc
+++ b/RedfishPkg/Redfish.fdf.inc
@@ -6,7 +6,7 @@
 # to be built in the firmware volume.
 #
 # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 #
 #    SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -20,4 +20,5 @@
   INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
   INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
   INF MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
+  INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
 !endif
-- 
2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115627): https://edk2.groups.io/g/devel/message/115627
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
  2024-02-20  6:41 [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
@ 2024-02-21  1:51 ` Chang, Abner via groups.io
  2024-02-22  9:16   ` Nickle Wang via groups.io
  0 siblings, 1 reply; 3+ messages in thread
From: Chang, Abner via groups.io @ 2024-02-21  1:51 UTC (permalink / raw)
  To: Nickle Wang, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez

[AMD Official Use Only - General]

One comment below.

> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com>
> Sent: Tuesday, February 20, 2024 2:41 PM
> To: devel@edk2.groups.io
> Cc: Igor Kulchytskyy <igork@ami.com>; Chang, Abner
> <Abner.Chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> implement Redfish HTTP protocol driver.
>
> Signed-off-by: Nickle Wang <nicklew@nvidia.com>
> Co-authored-by: Igor Kulchytskyy <igork@ami.com>
> Cc: Abner Chang <abner.chang@amd.com>
> Cc: Igor Kulchytskyy <igork@ami.com>
> Cc: Nick Ramirez <nramirez@nvidia.com>
> ---
>  RedfishPkg/RedfishPkg.dec                     |    7 +-
>  RedfishPkg/RedfishComponents.dsc.inc          |    3 +-
>  RedfishPkg/RedfishPkg.dsc                     |    2 +
>  RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf  |   73 +
>  RedfishPkg/RedfishHttpDxe/RedfishHttpData.h   |  256 ++++
>  RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h    |   44 +
>  .../RedfishHttpDxe/RedfishHttpOperation.h     |   76 +
>  RedfishPkg/RedfishHttpDxe/RedfishHttpData.c   |  667 ++++++++
>  RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c    | 1344
> +++++++++++++++++
>  .../RedfishHttpDxe/RedfishHttpOperation.c     |  693 +++++++++
>  RedfishPkg/Redfish.fdf.inc                    |    3 +-
>  11 files changed, 3164 insertions(+), 4 deletions(-)
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
>  create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
>
> diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
> index 9b424efdf3..114f8d2ad8 100644
> --- a/RedfishPkg/RedfishPkg.dec
> +++ b/RedfishPkg/RedfishPkg.dec
> @@ -157,8 +157,11 @@
>    # set to EFI_REST_EX_PROTOCOL.
>    #
>
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT3
> 2|0x00001009
> -  ## This is used to enable HTTP content encoding on Redfish communication.
> -
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BO
> OLEAN|0x0000100A
> +  #
> +  # This PCD string is introduced for platform developer to set the encoding
> method supported by BMC Redfish.
> +  # Currently only "None" and "gzip" are supported.
> +  #
> +
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|V
> OID*|0x0000100A
>    #
>    # Use below PCDs to control Redfhs HTTP protocol.
>    #
> diff --git a/RedfishPkg/RedfishComponents.dsc.inc
> b/RedfishPkg/RedfishComponents.dsc.inc
> index 464ffc8606..d6c5b73d7f 100644
> --- a/RedfishPkg/RedfishComponents.dsc.inc
> +++ b/RedfishPkg/RedfishComponents.dsc.inc
> @@ -7,7 +7,7 @@
>  # "RedfishDefines.dsc.inc".
>  #
>  # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
>  #
>  #    SPDX-License-Identifier: BSD-2-Clause-Patent
>  #
> @@ -28,4 +28,5 @@
>    RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
>    RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
>    MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> +  RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
>  !endif
> diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
> index 25ed193182..5849e7cf9e 100644
> --- a/RedfishPkg/RedfishPkg.dsc
> +++ b/RedfishPkg/RedfishPkg.dsc
> @@ -45,6 +45,8 @@
>
> UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesL
> ib.inf
>
> RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/
> PlatformCredentialLibNull.inf
>
> RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/
> RedfishContentCodingLibNull.inf
> +
> ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeR
> eportStatusCodeLib.inf
> +  SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
>
>    # NULL instance of IPMI related library.
>    IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> new file mode 100644
> index 0000000000..c7dfdffacf
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> @@ -0,0 +1,73 @@
> +## @file
> +#  RedfishHttpDxe is the DXE driver which provides
> +#  EdkIIRedfishHttpProtocol to EDK2 Redfish Feature
> +#  drivers for HTTP operation.
> +#
> +#  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x0001000b
> +  BASE_NAME                      = RedfishHttpDxe
> +  FILE_GUID                      = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = RedfishHttpEntryPoint
> +  UNLOAD_IMAGE                   = RedfishHttpDriverUnload
> +
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64 ARM AARCH64 RISCV64
> +#
> +
> +[Sources]
> +  RedfishHttpData.c
> +  RedfishHttpData.h
> +  RedfishHttpDxe.c
> +  RedfishHttpDxe.h
> +  RedfishHttpOperation.c
> +  RedfishHttpOperation.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  NetworkPkg/NetworkPkg.dec
> +  RedfishPkg/RedfishPkg.dec
> +
> +[LibraryClasses.ARM]
> +  ArmSoftFloatLib
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  RedfishContentCodingLib
> +  DebugLib
> +  HttpLib
> +  JsonLib
> +  MemoryAllocationLib
> +  PrintLib
> +  RedfishDebugLib
> +  ReportStatusCodeLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Protocols]
> +  gEdkIIRedfishHttpProtocolGuid             ## PRODUCED
> +  gEdkIIRedfishCredentialProtocolGuid       ## CONSUMES
> +  gEfiRestExProtocolGuid                    ## CONSUEMS
> +
> +[Pcd]
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond
> +  gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled
> +  gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding
> +
> +[Depex]
> +  TRUE
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> new file mode 100644
> index 0000000000..6be610142e
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> @@ -0,0 +1,256 @@
> +/** @file
> +  Definitions of RedfishHttpData
> +
> +  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_DATA_H_
> +#define EDKII_REDFISH_HTTP_DATA_H_
> +
> +#include "RedfishHttpDxe.h"
> +
> +#define REDFISH_HTTP_DRIVER_SIGNATURE   SIGNATURE_32 ('r', 'f', 'h', 'p')
> +#define REDFISH_HTTP_CACHE_SIGNATURE    SIGNATURE_32 ('r', 'f', 'c', 'h')
> +#define REDFISH_HTTP_SERVICE_SIGNATURE  SIGNATURE_32 ('r', 'f', 's', 'v')
> +#define REDFISH_HTTP_PAYLOAD_SIGNATURE  SIGNATURE_32 ('r', 'f', 'p', 'l')
> +#define REDFISH_HTTP_BASIC_AUTH_STR     "Basic "
> +
> +///
> +/// REDFISH_SERVICE_PRIVATE definition.
> +///
> +typedef struct {
> +  UINT32                  Signature;
> +  CHAR8                   *Host;
> +  CHAR8                   *HostName;
> +  CHAR8                   *BasicAuth;
> +  CHAR8                   *SessionToken;
> +  EFI_REST_EX_PROTOCOL    *RestEx;
> +} REDFISH_SERVICE_PRIVATE;
> +
> +///
> +/// REDFISH_PAYLOAD_PRIVATE definition.
> +///
> +typedef struct {
> +  UINT32                     Signature;
> +  REDFISH_SERVICE_PRIVATE    *Service;
> +  EDKII_JSON_VALUE           JsonValue;
> +} REDFISH_PAYLOAD_PRIVATE;
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_DATA
> +///
> +typedef struct {
> +  UINT32              Signature;
> +  LIST_ENTRY          List;
> +  EFI_STRING          Uri;
> +  UINTN               HitCount;
> +  REDFISH_RESPONSE    *Response;
> +} REDFISH_HTTP_CACHE_DATA;
> +
> +#define REDFISH_HTTP_CACHE_FROM_LIST(a)  CR (a,
> REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE)
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_LIST
> +///
> +typedef struct {
> +  LIST_ENTRY    Head;
> +  UINTN         Count;
> +  UINTN         Capacity;
> +} REDFISH_HTTP_CACHE_LIST;
> +
> +///
> +/// Definition of REDFISH_HTTP_RETRY_SETTING
> +///
> +typedef struct {
> +  UINT16    MaximumRetryGet;
> +  UINT16    MaximumRetryPut;
> +  UINT16    MaximumRetryPost;
> +  UINT16    MaximumRetryPatch;
> +  UINT16    MaximumRetryDelete;
> +  UINTN     RetryWait;
> +} REDFISH_HTTP_RETRY_SETTING;
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_PRIVATE
> +///
> +typedef struct {
> +  UINT32                               Signature;
> +  EFI_HANDLE                           ImageHandle;
> +  BOOLEAN                              CacheDisabled;
> +  EFI_EVENT                            NotifyEvent;
> +  REDFISH_HTTP_CACHE_LIST              CacheList;
> +  EDKII_REDFISH_HTTP_PROTOCOL          Protocol;
> +  EDKII_REDFISH_CREDENTIAL_PROTOCOL    *CredentialProtocol;
> +  REDFISH_HTTP_RETRY_SETTING           RetrySetting;
> +} REDFISH_HTTP_CACHE_PRIVATE;
> +
> +#define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a)  CR (a,
> REDFISH_HTTP_CACHE_PRIVATE, Protocol,
> REDFISH_HTTP_DRIVER_SIGNATURE)
> +
> +/**
> +  Search on given ListHeader for given URI string.
> +
> +  @param[in]    ListHeader  Target list to search.
> +  @param[in]    Uri         Target URI to search.
> +
> +  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found.
> +  @retval NULL                      No cache data with given URI is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindHttpCacheData (
> +  IN  LIST_ENTRY  *ListHeader,
> +  IN  EFI_STRING  Uri
> +  );
> +
> +/**
> +  This function copy the data in SrcResponse to DstResponse.
> +
> +  @param[in]  SrcResponse      Source Response to copy.
> +  @param[out] DstResponse      Destination Response.
> +
> +  @retval     EFI_SUCCESS      Response is copied successfully.
> +  @retval     Others           Error occurs.
> +
> +**/
> +EFI_STATUS
> +CopyRedfishResponse (
> +  IN  REDFISH_RESPONSE  *SrcResponse,
> +  OUT REDFISH_RESPONSE  *DstResponse
> +  );
> +
> +/**
> +  Release all cache from list.
> +
> +  @param[in]    CacheList    The list to be released.
> +
> +  @retval EFI_SUCCESS             All cache data are released.
> +  @retval EFI_INVALID_PARAMETER   CacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +ReleaseCacheList (
> +  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
> +  );
> +
> +/**
> +  Add new cache by given URI and HTTP response to specify List.
> +
> +  @param[in]    List      Target cache list to add.
> +  @param[in]    Uri       The URI string matching to this cache data.
> +  @param[in]    Response  HTTP response.
> +
> +  @retval EFI_SUCCESS   Cache data is added.
> +  @retval Others        Fail to add cache data.
> +
> +**/
> +EFI_STATUS
> +AddHttpCacheData (
> +  IN  REDFISH_HTTP_CACHE_LIST  *List,
> +  IN  EFI_STRING               Uri,
> +  IN  REDFISH_RESPONSE         *Response
> +  );
> +
> +/**
> +  Delete a cache data by given cache instance.
> +
> +  @param[in]    List    Target cache list to be removed.
> +  @param[in]    Data    Pointer to the instance to be deleted.
> +
> +  @retval EFI_SUCCESS   Cache data is removed.
> +  @retval Others        Fail to remove cache data.
> +
> +**/
> +EFI_STATUS
> +DeleteHttpCacheData (
> +  IN  REDFISH_HTTP_CACHE_LIST  *List,
> +  IN  REDFISH_HTTP_CACHE_DATA  *Data
> +  );
> +
> +/**
> +  This function release Redfish Payload.
> +
> +  @param[in]  Payload         Pointer to payload instance.
> +
> +  @retval     EFI_SUCCESS     Payload is released.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishPayload (
> +  IN REDFISH_PAYLOAD_PRIVATE  *Payload
> +  );
> +
> +/**
> +  This function creat new payload. Server and JsonObj are
> +  copied to newly created payload.
> +
> +  @param[in]  Service          Pointer to Service instance.
> +  @param[in]  JsonObj          Pointer to JSON object.
> +
> +  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload.
> +  @retval     NULL             Error occurs.
> +
> +**/
> +REDFISH_PAYLOAD_PRIVATE *
> +CreateRedfishPayload (
> +  IN REDFISH_SERVICE_PRIVATE  *Service,
> +  IN EDKII_JSON_VALUE         JsonValue
> +  );
> +
> +/**
> +  This function release Redfish Service.
> +
> +  @param[in]  Service         Pointer to service instance.
> +
> +  @retval     EFI_SUCCESS     Service is released.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishService (
> +  IN REDFISH_SERVICE_PRIVATE  *Service
> +  );
> +
> +/**
> +  This function creat new service. Host and HostName are copied to
> +  newly created service instance.
> +
> +  @param[in]  Host            Host string.
> +  @param[in]  HostName        Hostname string.
> +  @param[in]  BasicAuth       Basic Authorization string.
> +  @param[in]  SessionToken    Session token string.
> +  @param[in]  RestEx          Rest EX protocol instance.
> +
> +  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service.
> +  @retval     NULL             Error occurs.
> +
> +**/
> +REDFISH_SERVICE_PRIVATE *
> +CreateRedfishService (
> +  IN CHAR8                 *Host,
> +  IN CHAR8                 *HostName,
> +  IN CHAR8                 *BasicAuth OPTIONAL,
> +  IN CHAR8                 *SessionToken OPTIONAL,
> +  IN EFI_REST_EX_PROTOCOL  *RestEx
> +  );
> +
> +/**
> +  This function update session token in Redfish Service.
> +
> +  @param[in]  Service         Pointer to service instance.
> +  @param[in]  Token           Session token.
> +
> +  @retval     EFI_SUCCESS     Session token is updated.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +UpdateSessionToken (
> +  IN REDFISH_SERVICE_PRIVATE  *Service,
> +  IN CHAR8                    *Token
> +  );
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> new file mode 100644
> index 0000000000..cf6ba9cb47
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> @@ -0,0 +1,44 @@
> +/** @file
> +  Definitions of RedfishHttpDxe
> +
> +  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_DXE_H_
> +#define EDKII_REDFISH_HTTP_DXE_H_
> +
> +#include <Uefi.h>
> +#include <IndustryStandard/Http11.h>
> +
> +#include <Library/UefiLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/RedfishContentCodingLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/HttpLib.h>
> +#include <Library/JsonLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/RedfishDebugLib.h>
> +#include <Library/ReportStatusCodeLib.h>
> +#include <Library/PrintLib.h>
> +
> +#include <Protocol/Http.h>
> +#include <Protocol/EdkIIRedfishHttpProtocol.h>
> +#include <Protocol/EdkIIRedfishCredential.h>
> +#include <Protocol/RestEx.h>
> +
> +#define IS_EMPTY_STRING(a)  ((a) == NULL || (a)[0] == '\0')
> +#define REDFISH_HTTP_CACHE_LIST_SIZE      0x80
> +#define REDFISH_ERROR_MSG_MAX             128
> +#define REDFISH_DEBUG_STRING_LENGTH       200
> +#define REDFISH_HOST_NAME_MAX             64   // IPv6 maximum length (39)
> + "https://" (8) + port number (maximum 5)
> +#define REDFISH_HTTP_ERROR_REPORT         "Redfish HTTP %a failure(0x%x):
> %s"
> +#define REDFISH_HTTP_CACHE_DEBUG          DEBUG_MANAGEABILITY
> +#define REDFISH_HTTP_CACHE_DEBUG_DUMP     DEBUG_MANAGEABILITY
> +#define REDFISH_HTTP_CACHE_DEBUG_REQUEST  DEBUG_MANAGEABILITY
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> new file mode 100644
> index 0000000000..e4fc053e96
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> @@ -0,0 +1,76 @@
> +/** @file
> +  Definitions of RedfishHttpOperation
> +
> +  Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_OP_H_
> +#define EDKII_REDFISH_HTTP_OP_H_

Nickle, I missed this when I reviewed it offline.
Could you please update the macro to align with header file name, EDKII_REDFISH_HTTP_OPERATION_H_

Thanks
Abner


> +
> +#include "RedfishHttpDxe.h"
> +
> +#define REDFISH_CONTENT_LENGTH_SIZE              80
> +#define REDFISH_COMMON_HEADER_SIZE               5
> +#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR    "OData-Version"
> +#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE  "4.0"
> +#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE     "edk2redfish"
> +#define REDFISH_HTTP_HEADER_CONNECTION_STR       "Connection"
> +#define REDFISH_HTTP_HEADER_CONNECTION_VALUE     "Keep-Alive"
> +#define REDFISH_HTTP_CONTENT_ENCODING_NONE       "None"
> +
> +/**
> +  This function free resources in Request. Request is no longer available
> +  after this function returns successfully.
> +
> +  @param[in]  Request      HTTP request to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishRequest (
> +  IN  REDFISH_REQUEST  *Request
> +  );
> +
> +/**
> +  This function free resources in given Response.
> +
> +  @param[in]  Response     HTTP response to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishResponse (
> +  IN  REDFISH_RESPONSE  *Response
> +  );
> +
> +/**
> +  This function send Redfish request to Redfish service by calling
> +  Rest Ex protocol.
> +
> +  @param[in]   Service       Pointer to Redfish service.
> +  @param[in]   Uri           Uri of Redfish service.
> +  @param[in]   Method        HTTP method.
> +  @param[in]   Request     Request data. This is optional.
> +  @param[out]  Response    Redfish response data.
> +
> +  @retval     EFI_SUCCESS     Request is sent and received successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +HttpSendReceive (
> +  IN  REDFISH_SERVICE   Service,
> +  IN  EFI_STRING        Uri,
> +  IN  EFI_HTTP_METHOD   Method,
> +  IN  REDFISH_REQUEST   *Request  OPTIONAL,
> +  OUT REDFISH_RESPONSE  *Response
> +  );
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> new file mode 100644
> index 0000000000..bf95e9f8d4
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> @@ -0,0 +1,667 @@
> +/** @file
> +  RedfishHttpData handles internal data to support Redfish HTTP protocol.
> +
> +  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpData.h"
> +#include "RedfishHttpOperation.h"
> +
> +/**
> +  This function update session token in Redfish Service.
> +
> +  @param[in]  Service         Pointer to service instance.
> +  @param[in]  Token           Session token.
> +
> +  @retval     EFI_SUCCESS     Session token is updated.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +UpdateSessionToken (
> +  IN REDFISH_SERVICE_PRIVATE  *Service,
> +  IN CHAR8                    *Token
> +  )
> +{
> +  if ((Service == NULL) || IS_EMPTY_STRING (Token)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Service->SessionToken != NULL) {
> +    FreePool (Service->SessionToken);
> +  }
> +
> +  Service->SessionToken = AllocateCopyPool (AsciiStrSize (Token), Token);
> +  if (Service->SessionToken == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function release Redfish Service.
> +
> +  @param[in]  Service         Pointer to service instance.
> +
> +  @retval     EFI_SUCCESS     Service is released.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishService (
> +  IN REDFISH_SERVICE_PRIVATE  *Service
> +  )
> +{
> +  if (Service == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Service->Host != NULL) {
> +    FreePool (Service->Host);
> +  }
> +
> +  if (Service->HostName != NULL) {
> +    FreePool (Service->HostName);
> +  }
> +
> +  if (Service->BasicAuth != NULL) {
> +    ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth));
> +    FreePool (Service->BasicAuth);
> +  }
> +
> +  if (Service->SessionToken != NULL) {
> +    ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken));
> +    FreePool (Service->SessionToken);
> +  }
> +
> +  FreePool (Service);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function creat new service. Host and HostName are copied to
> +  newly created service instance.
> +
> +  @param[in]  Host            Host string.
> +  @param[in]  HostName        Hostname string.
> +  @param[in]  BasicAuth       Basic Authorization string.
> +  @param[in]  SessionToken    Session token string.
> +  @param[in]  RestEx          Rest EX protocol instance.
> +
> +  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service.
> +  @retval     NULL             Error occurs.
> +
> +**/
> +REDFISH_SERVICE_PRIVATE *
> +CreateRedfishService (
> +  IN CHAR8                 *Host,
> +  IN CHAR8                 *HostName,
> +  IN CHAR8                 *BasicAuth OPTIONAL,
> +  IN CHAR8                 *SessionToken OPTIONAL,
> +  IN EFI_REST_EX_PROTOCOL  *RestEx
> +  )
> +{
> +  REDFISH_SERVICE_PRIVATE  *NewService;
> +  UINTN                    AuthStrSize;
> +
> +  if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx
> == NULL)) {
> +    return NULL;
> +  }
> +
> +  NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE));
> +  if (NewService == NULL) {
> +    return NULL;
> +  }
> +
> +  NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE;
> +  NewService->Host      = AllocateCopyPool (AsciiStrSize (Host), Host);
> +  if (NewService->Host == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  NewService->HostName = AllocateCopyPool (AsciiStrSize (HostName),
> HostName);
> +  if (NewService->HostName == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  if (!IS_EMPTY_STRING (BasicAuth)) {
> +    AuthStrSize           = AsciiStrSize (BasicAuth) + AsciiStrLen
> (REDFISH_HTTP_BASIC_AUTH_STR);
> +    NewService->BasicAuth = AllocateZeroPool (AuthStrSize);
> +    if (NewService->BasicAuth == NULL) {
> +      goto ON_ERROR;
> +    }
> +
> +    AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a",
> REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth);
> +  }
> +
> +  if (!IS_EMPTY_STRING (SessionToken)) {
> +    NewService->SessionToken = AllocateCopyPool (AsciiStrSize
> (SessionToken), SessionToken);
> +    if (NewService->SessionToken == NULL) {
> +      goto ON_ERROR;
> +    }
> +  }
> +
> +  NewService->RestEx = RestEx;
> +
> +  return NewService;
> +
> +ON_ERROR:
> +
> +  ReleaseRedfishService (NewService);
> +
> +  return NULL;
> +}
> +
> +/**
> +  This function release Redfish Payload.
> +
> +  @param[in]  Payload         Pointer to payload instance.
> +
> +  @retval     EFI_SUCCESS     Payload is released.
> +  @retval     Others          Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishPayload (
> +  IN REDFISH_PAYLOAD_PRIVATE  *Payload
> +  )
> +{
> +  if (Payload == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Payload->Service != NULL) {
> +    ReleaseRedfishService (Payload->Service);
> +  }
> +
> +  if (Payload->JsonValue != NULL) {
> +    JsonValueFree (Payload->JsonValue);
> +  }
> +
> +  FreePool (Payload);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function creat new payload. Server and JsonObj are
> +  copied to newly created payload.
> +
> +  @param[in]  Service          Pointer to Service instance.
> +  @param[in]  JsonValue        Pointer to JSON value.
> +
> +  @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload.
> +  @retval     NULL                     Error occurs.
> +
> +**/
> +REDFISH_PAYLOAD_PRIVATE *
> +CreateRedfishPayload (
> +  IN REDFISH_SERVICE_PRIVATE  *Service,
> +  IN EDKII_JSON_VALUE         JsonValue
> +  )
> +{
> +  REDFISH_PAYLOAD_PRIVATE  *NewPayload;
> +
> +  if ((Service == NULL) || (JsonValue == NULL)) {
> +    return NULL;
> +  }
> +
> +  NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE));
> +  if (NewPayload == NULL) {
> +    return NULL;
> +  }
> +
> +  NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE;
> +  NewPayload->Service   = CreateRedfishService (Service->Host, Service-
> >HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx);
> +  if (NewPayload->Service == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  NewPayload->JsonValue = JsonValueClone (JsonValue);
> +  if (NewPayload->JsonValue == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  return NewPayload;
> +
> +ON_ERROR:
> +
> +  ReleaseRedfishPayload (NewPayload);
> +
> +  return NULL;
> +}
> +
> +/**
> +  This function copy the data in SrcResponse to DstResponse.
> +
> +  @param[in]  SrcResponse      Source Response to copy.
> +  @param[out] DstResponse      Destination Response.
> +
> +  @retval     EFI_SUCCESS      Response is copied successfully.
> +  @retval     Others           Error occurs.
> +
> +**/
> +EFI_STATUS
> +CopyRedfishResponse (
> +  IN  REDFISH_RESPONSE  *SrcResponse,
> +  OUT REDFISH_RESPONSE  *DstResponse
> +  )
> +{
> +  REDFISH_PAYLOAD_PRIVATE  *Payload;
> +  UINTN                    Index;
> +
> +  if ((SrcResponse == NULL) || (DstResponse == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (SrcResponse == DstResponse) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  //
> +  // Status code
> +  //
> +  if (SrcResponse->StatusCode != NULL) {
> +    DstResponse->StatusCode = AllocateCopyPool (sizeof
> (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
> +    if (DstResponse->StatusCode == NULL) {
> +      goto ON_ERROR;
> +    }
> +  }
> +
> +  //
> +  // Header
> +  //
> +  if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
> +    DstResponse->HeaderCount = 0;
> +    DstResponse->Headers     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> SrcResponse->HeaderCount);
> +    if (DstResponse->Headers == NULL) {
> +      goto ON_ERROR;
> +    }
> +
> +    for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
> +      DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldName), SrcResponse-
> >Headers[Index].FieldName);
> +      if (DstResponse->Headers[Index].FieldName == NULL) {
> +        goto ON_ERROR;
> +      }
> +
> +      DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldValue), SrcResponse-
> >Headers[Index].FieldValue);
> +      if (DstResponse->Headers[Index].FieldValue == NULL) {
> +        goto ON_ERROR;
> +      }
> +
> +      DstResponse->HeaderCount += 1;
> +    }
> +  }
> +
> +  //
> +  // Payload
> +  //
> +  if (SrcResponse->Payload != NULL) {
> +    Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload;
> +    if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> +      DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> +      goto ON_ERROR;
> +    }
> +
> +    DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload-
> >JsonValue);
> +    if (DstResponse->Payload  == NULL) {
> +      goto ON_ERROR;
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +
> +ON_ERROR:
> +
> +  ReleaseRedfishResponse (DstResponse);
> +
> +  return EFI_OUT_OF_RESOURCES;
> +}
> +
> +/**
> +  This function clone input response and return to caller
> +
> +  @param[in]  Response      Response to clone.
> +
> +  @retval     REDFISH_RESPONSE *  Response is cloned.
> +  @retval     NULL                Errors occur.
> +
> +**/
> +REDFISH_RESPONSE *
> +CloneRedfishResponse (
> +  IN REDFISH_RESPONSE  *Response
> +  )
> +{
> +  EFI_STATUS        Status;
> +  REDFISH_RESPONSE  *NewResponse;
> +
> +  if (Response == NULL) {
> +    return NULL;
> +  }
> +
> +  NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
> +  if (NewResponse == NULL) {
> +    return NULL;
> +  }
> +
> +  Status = CopyRedfishResponse (Response, NewResponse);
> +  if (EFI_ERROR (Status)) {
> +    FreePool (NewResponse);
> +    return NULL;
> +  }
> +
> +  return NewResponse;
> +}
> +
> +/**
> +  Release REDFISH_HTTP_CACHE_DATA resource
> +
> +  @param[in]    Data    Pointer to REDFISH_HTTP_CACHE_DATA instance
> +
> +  @retval EFI_SUCCESS             REDFISH_HTTP_CACHE_DATA is released
> successfully.
> +  @retval EFI_INVALID_PARAMETER   Data is NULL
> +
> +**/
> +EFI_STATUS
> +ReleaseHttpCacheData (
> +  IN REDFISH_HTTP_CACHE_DATA  *Data
> +  )
> +{
> +  if (Data == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (Data->Uri != NULL) {
> +    FreePool (Data->Uri);
> +  }
> +
> +  if (Data->Response != NULL) {
> +    ReleaseRedfishResponse (Data->Response);
> +    FreePool (Data->Response);
> +  }
> +
> +  FreePool (Data);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Create new cache data.
> +
> +  @param[in]    Uri       The URI string matching to this cache data.
> +  @param[in]    Response  HTTP response.
> +
> +  @retval REDFISH_HTTP_CACHE_DATA *   Pointer to newly created cache
> data.
> +  @retval NULL                        No memory available.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +NewHttpCacheData (
> +  IN  EFI_STRING        Uri,
> +  IN  REDFISH_RESPONSE  *Response
> +  )
> +{
> +  REDFISH_HTTP_CACHE_DATA  *NewData;
> +  UINTN                    Size;
> +
> +  if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> +    return NULL;
> +  }
> +
> +  NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
> +  if (NewData == NULL) {
> +    return NULL;
> +  }
> +
> +  NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE;
> +  Size               = StrSize (Uri);
> +  NewData->Uri       = AllocateCopyPool (Size, Uri);
> +  if (NewData->Uri == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  NewData->Response = Response;
> +  NewData->HitCount = 1;
> +
> +  return NewData;
> +
> +ON_ERROR:
> +
> +  if (NewData != NULL) {
> +    ReleaseHttpCacheData (NewData);
> +  }
> +
> +  return NULL;
> +}
> +
> +/**
> +  Search on given ListHeader for given URI string.
> +
> +  @param[in]    ListHeader  Target list to search.
> +  @param[in]    Uri         Target URI to search.
> +
> +  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found.
> +  @retval NULL                      No cache data with given URI is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindHttpCacheData (
> +  IN  LIST_ENTRY  *ListHeader,
> +  IN  EFI_STRING  Uri
> +  )
> +{
> +  LIST_ENTRY               *List;
> +  REDFISH_HTTP_CACHE_DATA  *Data;
> +
> +  if (IS_EMPTY_STRING (Uri)) {
> +    return NULL;
> +  }
> +
> +  if (IsListEmpty (ListHeader)) {
> +    return NULL;
> +  }
> +
> +  Data = NULL;
> +  List = GetFirstNode (ListHeader);
> +  while (!IsNull (ListHeader, List)) {
> +    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> +    if (StrCmp (Data->Uri, Uri) == 0) {
> +      return Data;
> +    }
> +
> +    List = GetNextNode (ListHeader, List);
> +  }
> +
> +  return NULL;
> +}
> +
> +/**
> +  Search on given ListHeader and return cache data with minimum hit count.
> +
> +  @param[in]    ListHeader  Target list to search.
> +
> +  @retval REDFISH_HTTP_CACHE_DATA   Target cache data is returned.
> +  @retval NULL                      No cache data is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindUnusedHttpCacheData (
> +  IN  LIST_ENTRY  *ListHeader
> +  )
> +{
> +  LIST_ENTRY               *List;
> +  REDFISH_HTTP_CACHE_DATA  *Data;
> +  REDFISH_HTTP_CACHE_DATA  *UnusedData;
> +  UINTN                    HitCount;
> +
> +  if (IsListEmpty (ListHeader)) {
> +    return NULL;
> +  }
> +
> +  Data       = NULL;
> +  UnusedData = NULL;
> +  HitCount   = 0;
> +
> +  List       = GetFirstNode (ListHeader);
> +  Data       = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +  UnusedData = Data;
> +  HitCount   = Data->HitCount;
> +  List       = GetNextNode (ListHeader, List);
> +
> +  while (!IsNull (ListHeader, List)) {
> +    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> +    if (Data->HitCount < HitCount) {
> +      HitCount   = Data->HitCount;
> +      UnusedData = Data;
> +    }
> +
> +    List = GetNextNode (ListHeader, List);
> +  }
> +
> +  return UnusedData;
> +}
> +
> +/**
> +  Delete a cache data by given cache instance.
> +
> +  @param[in]    List    Target cache list to be removed.
> +  @param[in]    Data    Pointer to the instance to be deleted.
> +
> +  @retval EFI_SUCCESS   Cache data is removed.
> +  @retval Others        Fail to remove cache data.
> +
> +**/
> +EFI_STATUS
> +DeleteHttpCacheData (
> +  IN  REDFISH_HTTP_CACHE_LIST  *List,
> +  IN  REDFISH_HTTP_CACHE_DATA  *Data
> +  )
> +{
> +  if ((List == NULL) || (Data == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__,
> Data->Uri));
> +
> +  RemoveEntryList (&Data->List);
> +  --List->Count;
> +
> +  return ReleaseHttpCacheData (Data);
> +}
> +
> +/**
> +  Add new cache by given URI and HTTP response to specify List.
> +
> +  @param[in]    List      Target cache list to add.
> +  @param[in]    Uri       The URI string matching to this cache data.
> +  @param[in]    Response  HTTP response.
> +
> +  @retval EFI_SUCCESS   Cache data is added.
> +  @retval Others        Fail to add cache data.
> +
> +**/
> +EFI_STATUS
> +AddHttpCacheData (
> +  IN  REDFISH_HTTP_CACHE_LIST  *List,
> +  IN  EFI_STRING               Uri,
> +  IN  REDFISH_RESPONSE         *Response
> +  )
> +{
> +  REDFISH_HTTP_CACHE_DATA  *NewData;
> +  REDFISH_HTTP_CACHE_DATA  *OldData;
> +  REDFISH_HTTP_CACHE_DATA  *UnusedData;
> +  REDFISH_RESPONSE         *NewResponse;
> +
> +  if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // If same cache data exist, replace it with latest one.
> +  //
> +  OldData = FindHttpCacheData (&List->Head, Uri);
> +  if (OldData != NULL) {
> +    DeleteHttpCacheData (List, OldData);
> +  }
> +
> +  //
> +  // Check capacity
> +  //
> +  if (List->Count >= List->Capacity) {
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused
> cache\n", __func__));
> +    UnusedData = FindUnusedHttpCacheData (&List->Head);
> +    if (UnusedData == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    DeleteHttpCacheData (List, UnusedData);
> +  }
> +
> +  //
> +  // Clone a local copy
> +  //
> +  NewResponse = CloneRedfishResponse (Response);
> +  if (NewResponse == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  NewData = NewHttpCacheData (Uri, NewResponse);
> +  if (NewData == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  InsertTailList (&List->Head, &NewData->List);
> +  ++List->Count;
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n",
> __func__, List->Count, List->Capacity, NewData->Uri));
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Release all cache from list.
> +
> +  @param[in]    CacheList    The list to be released.
> +
> +  @retval EFI_SUCCESS             All cache data are released.
> +  @retval EFI_INVALID_PARAMETER   CacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +ReleaseCacheList (
> +  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
> +  )
> +{
> +  LIST_ENTRY               *List;
> +  LIST_ENTRY               *Next;
> +  REDFISH_HTTP_CACHE_DATA  *Data;
> +
> +  if (CacheList == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (IsListEmpty (&CacheList->Head)) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  Data = NULL;
> +  Next = NULL;
> +  List = GetFirstNode (&CacheList->Head);
> +  while (!IsNull (&CacheList->Head, List)) {
> +    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +    Next = GetNextNode (&CacheList->Head, List);
> +
> +    DeleteHttpCacheData (CacheList, Data);
> +
> +    List = Next;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> new file mode 100644
> index 0000000000..39958d4865
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> @@ -0,0 +1,1344 @@
> +/** @file
> +  RedfishHttpDxe produces EdkIIRedfishHttpProtocol
> +  for EDK2 Redfish Feature driver to do HTTP operations.
> +
> +  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpDxe.h"
> +#include "RedfishHttpData.h"
> +#include "RedfishHttpOperation.h"
> +
> +REDFISH_HTTP_CACHE_PRIVATE  *mRedfishHttpCachePrivate = NULL;
> +
> +/**
> +  Debug output the cache list.
> +
> +  @param[in]    Msg            Debug message string.
> +  @param[in]    ErrorLevel     Output error level.
> +  @param[in]    CacheList      Target list to dump.
> +
> +  @retval EFI_SUCCESS             Debug dump finished.
> +  @retval EFI_INVALID_PARAMETER   HttpCacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +DebugPrintHttpCacheList (
> +  IN  CONST CHAR8              *Msg,
> +  IN  UINTN                    ErrorLevel,
> +  IN  REDFISH_HTTP_CACHE_LIST  *CacheList
> +  )
> +{
> +  LIST_ENTRY               *List;
> +  REDFISH_HTTP_CACHE_DATA  *Data;
> +  UINTN                    Index;
> +
> +  if (CacheList == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (!IS_EMPTY_STRING (Msg)) {
> +    DEBUG ((ErrorLevel, "%a\n", Msg));
> +  }
> +
> +  if (IsListEmpty (&CacheList->Head)) {
> +    DEBUG ((ErrorLevel, "list is empty\n"));
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count,
> CacheList->Capacity));
> +  Data  = NULL;
> +  Index = 0;
> +  List  = GetFirstNode (&CacheList->Head);
> +  while (!IsNull (&CacheList->Head, List)) {
> +    Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> +    DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data-
> >HitCount));
> +
> +    List = GetNextNode (&CacheList->Head, List);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Check HTTP status code to see if we like to retry HTTP request or not.
> +
> +  @param[in]  StatusCode      HTTP status code.
> +
> +  @retval     BOOLEAN         Return true when we like to retry request.
> +                              Return false when we don't want to retry request.
> +
> +**/
> +BOOLEAN
> +RedfishRetryRequired (
> +  IN EFI_HTTP_STATUS_CODE  *StatusCode
> +  )
> +{
> +  if (StatusCode == NULL) {
> +    return TRUE;
> +  }
> +
> +  if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) ||
> +      (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS))
> +  {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +
> +  Convert Unicode string to ASCII string. It's call responsibility to release
> returned buffer.
> +
> +  @param[in]  UnicodeStr      Unicode string to convert.
> +
> +  @retval     CHAR8 *         ASCII string returned.
> +  @retval     NULL            Errors occur.
> +
> +**/
> +CHAR8 *
> +StringUnicodeToAscii (
> +  IN EFI_STRING  UnicodeStr
> +  )
> +{
> +  CHAR8       *AsciiStr;
> +  UINTN       AsciiStrSize;
> +  EFI_STATUS  Status;
> +
> +  if (IS_EMPTY_STRING (UnicodeStr)) {
> +    return NULL;
> +  }
> +
> +  AsciiStrSize = StrLen (UnicodeStr) + 1;
> +  AsciiStr     = AllocateZeroPool (AsciiStrSize);
> +  if (AsciiStr == NULL) {
> +    return NULL;
> +  }
> +
> +  Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
> +    FreePool (AsciiStr);
> +    return NULL;
> +  }
> +
> +  return AsciiStr;
> +}
> +
> +/**
> +  Return HTTP method in ASCII string. Caller does not need
> +  to free returned string buffer.
> +
> +  @param[in]  Method         HTTP method.
> +
> +  @retval CHAR8 *   Method in string.
> +**/
> +CHAR8 *
> +HttpMethodToString (
> +  IN  EFI_HTTP_METHOD  Method
> +  )
> +{
> +  switch (Method) {
> +    case HttpMethodGet:
> +      return HTTP_METHOD_GET;
> +      break;
> +    case HttpMethodPost:
> +      return HTTP_METHOD_POST;
> +      break;
> +    case HttpMethodPatch:
> +      return HTTP_METHOD_PATCH;
> +      break;
> +    case HttpMethodPut:
> +      return HTTP_METHOD_PUT;
> +      break;
> +    case HttpMethodDelete:
> +      return HTTP_METHOD_DELETE;
> +      break;
> +    default:
> +      break;
> +  }
> +
> +  return "Unknown";
> +}
> +
> +/**
> +  Report HTTP communication error via report status code.
> +
> +  @param[in]  Method         HTTP method.
> +  @param[in]  Uri            The URI which has failure.
> +  @param[in]  HttpStatusCode HTTP status code.
> +
> +**/
> +VOID
> +ReportHttpError (
> +  IN  EFI_HTTP_METHOD       Method,
> +  IN  EFI_STRING            Uri,
> +  IN  EFI_HTTP_STATUS_CODE  *HttpStatusCode  OPTIONAL
> +  )
> +{
> +  CHAR8  ErrorMsg[REDFISH_ERROR_MSG_MAX];
> +
> +  if (IS_EMPTY_STRING (Uri)) {
> +    DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n",
> __func__));
> +    return;
> +  }
> +
> +  //
> +  // Report failure of URI and HTTP status code.
> +  //
> +  AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT,
> HttpMethodToString (Method), (HttpStatusCode == NULL ?
> HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri);
> +  DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg));
> +  //
> +  // TODO:
> +  // Below PI status code is approved by PIWG and wait for specification
> published.
> +  // We will uncomment below report status code after PI status code get
> published.
> +  // REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4483
> +  //
> +  // REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
> +  //  EFI_ERROR_CODE | EFI_ERROR_MAJOR,
> +  //  EFI_COMPUTING_UNIT_MANAGEABILITY |
> EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR,
> +  //  ErrorMsg,
> +  //  AsciiStrSize (ErrorMsg)
> +  //  );
> +}
> +
> +/**
> +  This function create Redfish service. It's caller's responsibility to free
> returned
> +  Redfish service by calling FreeService ().
> +
> +  @param[in]  This                       Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> +  @param[in]  RedfishConfigServiceInfo   Redfish config service information.
> +
> +  @retval     REDFISH_SERVICE  Redfish service is created.
> +  @retval     NULL             Errors occur.
> +
> +**/
> +REDFISH_SERVICE
> +EFIAPI
> +RedfishCreateRedfishService (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL         *This,
> +  IN  REDFISH_CONFIG_SERVICE_INFORMATION  *RedfishConfigServiceInfo
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +  REDFISH_SERVICE_PRIVATE     *NewService;
> +  CHAR8                       *AsciiLocation;
> +  CHAR8                       *Host;
> +  CHAR8                       *BasicAuthString;
> +  UINTN                       BasicAuthStrSize;
> +  CHAR8                       *EncodedAuthString;
> +  UINTN                       EncodedAuthStrSize;
> +  EDKII_REDFISH_AUTH_METHOD   AuthMethod;
> +  CHAR8                       *Username;
> +  CHAR8                       *Password;
> +  UINTN                       UsernameSize;
> +  UINTN                       PasswordSize;
> +  EFI_REST_EX_PROTOCOL        *RestEx;
> +
> +  if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) {
> +    return NULL;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n",
> __func__, RedfishConfigServiceInfo->RedfishServiceLocation));
> +
> +  Private            = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  BasicAuthString    = NULL;
> +  EncodedAuthString  = NULL;
> +  Username           = NULL;
> +  Password           = NULL;
> +  NewService         = NULL;
> +  AsciiLocation      = NULL;
> +  Host               = NULL;
> +  BasicAuthStrSize   = 0;
> +  EncodedAuthStrSize = 0;
> +  UsernameSize       = 0;
> +  PasswordSize       = 0;
> +
> +  //
> +  // Build host and host name from service location
> +  //
> +  if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation))
> {
> +    AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo-
> >RedfishServiceLocation);
> +    if (AsciiLocation == NULL) {
> +      goto ON_RELEASE;
> +    }
> +
> +    Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX);
> +    if (AsciiLocation == NULL) {
> +      goto ON_RELEASE;
> +    }
> +
> +    if (RedfishConfigServiceInfo->RedfishServiceUseHttps) {
> +      AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a",
> AsciiLocation);
> +    } else {
> +      AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a",
> AsciiLocation);
> +    }
> +
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__,
> Host));
> +  }
> +
> +  //
> +  // Find Rest Ex protocol
> +  //
> +  if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) {
> +    Status = gBS->HandleProtocol (
> +                    RedfishConfigServiceInfo->RedfishServiceRestExHandle,
> +                    &gEfiRestExProtocolGuid,
> +                    (VOID **)&RestEx
> +                    );
> +  } else {
> +    DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n",
> __func__));
> +    goto ON_RELEASE;
> +  }
> +
> +  //
> +  // Get credential
> +  //
> +  if (Private->CredentialProtocol == NULL) {
> +    //
> +    // No credential available on this system.
> +    //
> +    DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n",
> __func__));
> +  } else {
> +    Status = Private->CredentialProtocol->GetAuthInfo (
> +                                            Private->CredentialProtocol,
> +                                            &AuthMethod,
> +                                            &Username,
> +                                            &Password
> +                                            );
> +    if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) ||
> IS_EMPTY_STRING (Password)) {
> +      DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information:
> %r\n", __func__, Status));
> +      goto ON_RELEASE;
> +    } else {
> +      DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x
> username: %a password: %a\n", __func__, AuthMethod, Username,
> Password));
> +
> +      //
> +      // Perform base64 encoding (RFC 7617)
> +      //
> +      UsernameSize     = AsciiStrSize (Username);
> +      PasswordSize     = AsciiStrSize (Password);
> +      BasicAuthStrSize =  UsernameSize + PasswordSize;  // one byte taken from
> null-terminator for ':'
> +      BasicAuthString  = AllocateZeroPool (BasicAuthStrSize);
> +      if (BasicAuthString == NULL) {
> +        goto ON_RELEASE;
> +      }
> +
> +      AsciiSPrint (
> +        BasicAuthString,
> +        BasicAuthStrSize,
> +        "%a:%a",
> +        Username,
> +        Password
> +        );
> +
> +      Status = Base64Encode (
> +                 (CONST UINT8 *)BasicAuthString,
> +                 BasicAuthStrSize,
> +                 EncodedAuthString,
> +                 &EncodedAuthStrSize
> +                 );
> +      if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) {
> +        EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize);
> +        if (EncodedAuthString == NULL) {
> +          goto ON_RELEASE;
> +        }
> +
> +        Status = Base64Encode (
> +                   (CONST UINT8 *)BasicAuthString,
> +                   BasicAuthStrSize,
> +                   EncodedAuthString,
> +                   &EncodedAuthStrSize
> +                   );
> +        if (EFI_ERROR (Status)) {
> +          DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> Status));
> +        }
> +
> +        DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization:
> %a\n", __func__, EncodedAuthString));
> +      } else {
> +        DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> Status));
> +        goto ON_RELEASE;
> +      }
> +    }
> +  }
> +
> +  NewService = CreateRedfishService (Host, AsciiLocation,
> EncodedAuthString, NULL, RestEx);
> +  if (NewService == NULL) {
> +    DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__));
> +  }
> +
> +ON_RELEASE:
> +
> +  if (BasicAuthString != NULL) {
> +    ZeroMem (BasicAuthString, BasicAuthStrSize);
> +    FreePool (BasicAuthString);
> +  }
> +
> +  if (EncodedAuthString != NULL) {
> +    ZeroMem (BasicAuthString, EncodedAuthStrSize);
> +    FreePool (EncodedAuthString);
> +  }
> +
> +  if (Username != NULL) {
> +    ZeroMem (Username, UsernameSize);
> +    FreePool (Username);
> +  }
> +
> +  if (Password != NULL) {
> +    ZeroMem (Password, PasswordSize);
> +    FreePool (Password);
> +  }
> +
> +  if (AsciiLocation != NULL) {
> +    FreePool (AsciiLocation);
> +  }
> +
> +  if (Host != NULL) {
> +    FreePool (Host);
> +  }
> +
> +  return NewService;
> +}
> +
> +/**
> +  This function free resources in Redfish service. RedfishService is no longer
> available
> +  after this function returns successfully.
> +
> +  @param[in]  This            Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> +  @param[in]  RedfishService  Pointer to Redfish service to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeRedfishService (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              RedfishService
> +  )
> +{
> +  REDFISH_SERVICE_PRIVATE  *Service;
> +
> +  if ((This == NULL) || (RedfishService == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Service = (REDFISH_SERVICE_PRIVATE *)RedfishService;
> +  if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
> +    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> +  }
> +
> +  return ReleaseRedfishService (Service);
> +}
> +
> +/**
> +  This function returns JSON value in given RedfishPayload. Returned JSON
> value
> +  is a reference to the JSON value in RedfishPayload. Any modification to
> returned
> +  JSON value will change JSON value in RedfishPayload.
> +
> +  @param[in]  This            Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> +  @param[in]  RedfishPayload  Pointer to Redfish payload.
> +
> +  @retval     EDKII_JSON_VALUE   JSON value is returned.
> +  @retval     NULL               Errors occur.
> +
> +**/
> +EDKII_JSON_VALUE
> +EFIAPI
> +RedfishJsonInRedfishPayload (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_PAYLOAD              RedfishPayload
> +  )
> +{
> +  REDFISH_PAYLOAD_PRIVATE  *Payload;
> +
> +  if ((This == NULL) || (RedfishPayload == NULL)) {
> +    return NULL;
> +  }
> +
> +  Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload;
> +  if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> +    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> +  }
> +
> +  return Payload->JsonValue;
> +}
> +
> +/**
> +  Perform HTTP GET to Get redfish resource from given resource URI with
> +  cache mechanism supported. It's caller's responsibility to free Response
> +  by calling FreeResponse ().
> +
> +  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Service       Redfish service instance to perform HTTP GET.
> +  @param[in]  Uri           Target resource URI.
> +  @param[in]  Request       Additional request context. This is optional.
> +  @param[out] Response      HTTP response from redfish service.
> +  @param[in]  UseCache      If it is TRUE, this function will search for
> +                            cache first. If it is FALSE, this function
> +                            will query Redfish URI directly.
> +
> +  @retval     EFI_SUCCESS     Resrouce is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishGetResource (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              Service,
> +  IN  EFI_STRING                   Uri,
> +  IN  REDFISH_REQUEST              *Request OPTIONAL,
> +  OUT REDFISH_RESPONSE             *Response,
> +  IN  BOOLEAN                      UseCache
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  REDFISH_HTTP_CACHE_DATA     *CacheData;
> +  UINTN                       RetryCount;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n",
> __func__, Uri, (UseCache ? "true" : "false")));
> +
> +  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  CacheData  = NULL;
> +  RetryCount = 0;
> +  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +
> +  if (Private->CacheDisabled) {
> +    UseCache = FALSE;
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by
> PCD!\n", __func__));
> +  }
> +
> +  //
> +  // Search for cache list.
> +  //
> +  if (UseCache) {
> +    CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> +    if (CacheData != NULL) {
> +      DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit!
> %s\n", __func__, Uri));
> +
> +      //
> +      // Copy cached response to caller's buffer.
> +      //
> +      Status               = CopyRedfishResponse (CacheData->Response, Response);
> +      CacheData->HitCount += 1;
> +      return Status;
> +    }
> +  }
> +
> +  //
> +  // Get resource from redfish service.
> +  //
> +  do {
> +    RetryCount += 1;
> +    Status      = HttpSendReceive (
> +                    Service,
> +                    Uri,
> +                    HttpMethodGet,
> +                    Request,
> +                    Response
> +                    );
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> +    if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryGet)) {
> +      break;
> +    }
> +
> +    //
> +    // Retry when BMC is not ready.
> +    //
> +    if ((Response->StatusCode != NULL)) {
> +      DEBUG_CODE (
> +        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +        );
> +
> +      if (!RedfishRetryRequired (Response->StatusCode)) {
> +        break;
> +      }
> +
> +      //
> +      // Release response for next round of request.
> +      //
> +      This->FreeResponse (This, Response);
> +    }
> +
> +    DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryGet));
> +    if (Private->RetrySetting.RetryWait > 0) {
> +      gBS->Stall (Private->RetrySetting.RetryWait);
> +    }
> +  } while (TRUE);
> +
> +  if (EFI_ERROR (Status)) {
> +    DEBUG_CODE (
> +      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +      );
> +    //
> +    // Report status code for Redfish failure
> +    //
> +    ReportHttpError (HttpMethodGet, Uri, Response->StatusCode);
> +    DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri,
> RetryCount, Private->RetrySetting.MaximumRetryGet, Status));
> +    goto ON_RELEASE;
> +  }
> +
> +  if (!Private->CacheDisabled) {
> +    //
> +    // Keep response in cache list
> +    //
> +    Status = AddHttpCacheData (&Private->CacheList, Uri, Response);
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri,
> Status));
> +      goto ON_RELEASE;
> +    }
> +
> +    DEBUG_CODE (
> +      DebugPrintHttpCacheList (__func__,
> REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList);
> +      );
> +  }
> +
> +ON_RELEASE:
> +
> +  return Status;
> +}
> +
> +/**
> +  This function free resources in Request. Request is no longer available
> +  after this function returns successfully.
> +
> +  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Request      HTTP request to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeRequest (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_REQUEST              *Request
> +  )
> +{
> +  if ((This == NULL) || (Request == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> +
> +  return ReleaseRedfishRequest (Request);
> +}
> +
> +/**
> +  This function free resources in given Response.
> +
> +  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Response     HTTP response to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeResponse (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_RESPONSE             *Response
> +  )
> +{
> +  if ((This == NULL) || (Response == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> +
> +  return ReleaseRedfishResponse (Response);
> +}
> +
> +/**
> +  This function expire the cached response of given URI.
> +
> +  @param[in]  This         Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Uri          Target response of URI.
> +
> +  @retval     EFI_SUCCESS     Target response is expired successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishExpireResponse (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  EFI_STRING                   Uri
> +  )
> +{
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +  REDFISH_HTTP_CACHE_DATA     *CacheData;
> +
> +  if ((This == NULL) || IS_EMPTY_STRING (Uri)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__,
> Uri));
> +
> +  Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +
> +  CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> +  if (CacheData == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  return DeleteHttpCacheData (&Private->CacheList, CacheData);
> +}
> +
> +/**
> +  Perform HTTP PATCH to send redfish resource to given resource URI.
> +  It's caller's responsibility to free Response by calling FreeResponse ().
> +
> +  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Service       Redfish service instance to perform HTTP PATCH.
> +  @param[in]  Uri           Target resource URI.
> +  @param[in]  Content       Data to patch.
> +  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
> +                            This is optional. When ContentSize is 0, ContentSize
> +                            is the size of Content.
> +  @param[in]  ContentType   Type of the Content to be send to Redfish
> service.
> +                            This is optional. When ContentType is NULL, content
> +                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
> +  @param[out] Response      HTTP response from redfish service.
> +
> +  @retval     EFI_SUCCESS     Resrouce is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPatchResource (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              Service,
> +  IN  EFI_STRING                   Uri,
> +  IN  CHAR8                        *Content,
> +  IN  UINTN                        ContentSize OPTIONAL,
> +  IN  CHAR8                        *ContentType OPTIONAL,
> +  OUT REDFISH_RESPONSE             *Response
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  UINTN                       RetryCount;
> +  REDFISH_REQUEST             Request;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__,
> Uri));
> +
> +  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  RetryCount = 0;
> +  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> +  Request.Content       = Content;
> +  Request.ContentLength = ContentSize;
> +  Request.ContentType   = ContentType;
> +
> +  //
> +  // Patch resource to redfish service.
> +  //
> +  do {
> +    RetryCount += 1;
> +    Status      = HttpSendReceive (
> +                    Service,
> +                    Uri,
> +                    HttpMethodPatch,
> +                    &Request,
> +                    Response
> +                    );
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> +    if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPatch)) {
> +      break;
> +    }
> +
> +    //
> +    // Retry when BMC is not ready.
> +    //
> +    if ((Response->StatusCode != NULL)) {
> +      DEBUG_CODE (
> +        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +        );
> +
> +      if (!RedfishRetryRequired (Response->StatusCode)) {
> +        break;
> +      }
> +
> +      //
> +      // Release response for next round of request.
> +      //
> +      This->FreeResponse (This, Response);
> +    }
> +
> +    DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry
> (%d/%d)\n", __func__, RetryCount, Private-
> >RetrySetting.MaximumRetryPatch));
> +    if (Private->RetrySetting.RetryWait > 0) {
> +      gBS->Stall (Private->RetrySetting.RetryWait);
> +    }
> +  } while (TRUE);
> +
> +  //
> +  // Redfish resource is updated. Automatically expire the cached response
> +  // so application can directly get resource from Redfish service again.
> +  //
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> +  RedfishExpireResponse (This, Uri);
> +
> +  if (EFI_ERROR (Status)) {
> +    DEBUG_CODE (
> +      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +      );
> +    //
> +    // Report status code for Redfish failure
> +    //
> +    ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode);
> +    DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status));
> +    goto ON_RELEASE;
> +  }
> +
> +ON_RELEASE:
> +
> +  return Status;
> +}
> +
> +/**
> +  Perform HTTP PUT to send redfish resource to given resource URI.
> +  It's caller's responsibility to free Response by calling FreeResponse ().
> +
> +  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Service       Redfish service instance to perform HTTP PUT.
> +  @param[in]  Uri           Target resource URI.
> +  @param[in]  Content       Data to put.
> +  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
> +                            This is optional. When ContentSize is 0, ContentSize
> +                            is the size of Content.
> +  @param[in]  ContentType   Type of the Content to be send to Redfish
> service.
> +                            This is optional. When ContentType is NULL, content
> +                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
> +  @param[out] Response      HTTP response from redfish service.
> +
> +  @retval     EFI_SUCCESS     Resrouce is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPutResource (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              Service,
> +  IN  EFI_STRING                   Uri,
> +  IN  CHAR8                        *Content,
> +  IN  UINTN                        ContentSize OPTIONAL,
> +  IN  CHAR8                        *ContentType OPTIONAL,
> +  OUT REDFISH_RESPONSE             *Response
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  UINTN                       RetryCount;
> +  REDFISH_REQUEST             Request;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__,
> Uri));
> +
> +  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  RetryCount = 0;
> +  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> +  Request.Content       = Content;
> +  Request.ContentLength = ContentSize;
> +  Request.ContentType   = ContentType;
> +
> +  //
> +  // Patch resource to redfish service.
> +  //
> +  do {
> +    RetryCount += 1;
> +    Status      = HttpSendReceive (
> +                    Service,
> +                    Uri,
> +                    HttpMethodPut,
> +                    &Request,
> +                    Response
> +                    );
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> +    if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPut)) {
> +      break;
> +    }
> +
> +    //
> +    // Retry when BMC is not ready.
> +    //
> +    if ((Response->StatusCode != NULL)) {
> +      DEBUG_CODE (
> +        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +        );
> +
> +      if (!RedfishRetryRequired (Response->StatusCode)) {
> +        break;
> +      }
> +
> +      //
> +      // Release response for next round of request.
> +      //
> +      This->FreeResponse (This, Response);
> +    }
> +
> +    DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryPut));
> +    if (Private->RetrySetting.RetryWait > 0) {
> +      gBS->Stall (Private->RetrySetting.RetryWait);
> +    }
> +  } while (TRUE);
> +
> +  //
> +  // Redfish resource is updated. Automatically expire the cached response
> +  // so application can directly get resource from Redfish service again.
> +  //
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> +  RedfishExpireResponse (This, Uri);
> +
> +  if (EFI_ERROR (Status)) {
> +    DEBUG_CODE (
> +      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +      );
> +    //
> +    // Report status code for Redfish failure
> +    //
> +    ReportHttpError (HttpMethodPut, Uri, Response->StatusCode);
> +    DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status));
> +    goto ON_RELEASE;
> +  }
> +
> +ON_RELEASE:
> +
> +  return Status;
> +}
> +
> +/**
> +  Perform HTTP POST to send redfish resource to given resource URI.
> +  It's caller's responsibility to free Response by calling FreeResponse ().
> +
> +  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Service       Redfish service instance to perform HTTP POST.
> +  @param[in]  Uri           Target resource URI.
> +  @param[in]  Content       Data to post.
> +  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
> +                            This is optional. When ContentSize is 0, ContentSize
> +                            is the size of Content.
> +  @param[in]  ContentType   Type of the Content to be send to Redfish
> service.
> +                            This is optional. When ContentType is NULL, content
> +                            type HTTP_CONTENT_TYPE_APP_JSON will be used.
> +  @param[out] Response      HTTP response from redfish service.
> +
> +  @retval     EFI_SUCCESS     Resrouce is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPostResource (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              Service,
> +  IN  EFI_STRING                   Uri,
> +  IN  CHAR8                        *Content,
> +  IN  UINTN                        ContentSize OPTIONAL,
> +  IN  CHAR8                        *ContentType OPTIONAL,
> +  OUT REDFISH_RESPONSE             *Response
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  UINTN                       RetryCount;
> +  REDFISH_REQUEST             Request;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__,
> Uri));
> +
> +  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  RetryCount = 0;
> +  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> +  Request.Content       = Content;
> +  Request.ContentLength = ContentSize;
> +  Request.ContentType   = ContentType;
> +
> +  //
> +  // Patch resource to redfish service.
> +  //
> +  do {
> +    RetryCount += 1;
> +    Status      = HttpSendReceive (
> +                    Service,
> +                    Uri,
> +                    HttpMethodPost,
> +                    &Request,
> +                    Response
> +                    );
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> +    if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPost)) {
> +      break;
> +    }
> +
> +    //
> +    // Retry when BMC is not ready.
> +    //
> +    if ((Response->StatusCode != NULL)) {
> +      DEBUG_CODE (
> +        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +        );
> +
> +      if (!RedfishRetryRequired (Response->StatusCode)) {
> +        break;
> +      }
> +
> +      //
> +      // Release response for next round of request.
> +      //
> +      This->FreeResponse (This, Response);
> +    }
> +
> +    DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryPost));
> +    if (Private->RetrySetting.RetryWait > 0) {
> +      gBS->Stall (Private->RetrySetting.RetryWait);
> +    }
> +  } while (TRUE);
> +
> +  //
> +  // Redfish resource is updated. Automatically expire the cached response
> +  // so application can directly get resource from Redfish service again.
> +  //
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> +  RedfishExpireResponse (This, Uri);
> +
> +  if (EFI_ERROR (Status)) {
> +    DEBUG_CODE (
> +      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +      );
> +    //
> +    // Report status code for Redfish failure
> +    //
> +    ReportHttpError (HttpMethodPost, Uri, Response->StatusCode);
> +    DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status));
> +    goto ON_RELEASE;
> +  }
> +
> +ON_RELEASE:
> +
> +  return Status;
> +}
> +
> +/**
> +  Perform HTTP DELETE to delete redfish resource on given resource URI.
> +  It's caller's responsibility to free Response by calling FreeResponse ().
> +
> +  @param[in]  This          Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> +  @param[in]  Service       Redfish service instance to perform HTTP DELETE.
> +  @param[in]  Uri           Target resource URI.
> +  @param[in]  Content       JSON represented properties to be deleted. This is
> +                            optional.
> +  @param[in]  ContentSize   Size of the Content to be send to Redfish service.
> +                            This is optional. When ContentSize is 0, ContentSize
> +                            is the size of Content if Content is not NULL.
> +  @param[in]  ContentType   Type of the Content to be send to Redfish
> service.
> +                            This is optional. When Content is not NULL and
> +                            ContentType is NULL, content type
> HTTP_CONTENT_TYPE_APP_JSON
> +                            will be used.
> +  @param[out] Response      HTTP response from redfish service.
> +
> +  @retval     EFI_SUCCESS     Resrouce is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishDeleteResource (
> +  IN  EDKII_REDFISH_HTTP_PROTOCOL  *This,
> +  IN  REDFISH_SERVICE              Service,
> +  IN  EFI_STRING                   Uri,
> +  IN  CHAR8                        *Content OPTIONAL,
> +  IN  UINTN                        ContentSize OPTIONAL,
> +  IN  CHAR8                        *ContentType OPTIONAL,
> +  OUT REDFISH_RESPONSE             *Response
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  UINTN                       RetryCount;
> +  REDFISH_REQUEST             Request;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__,
> Uri));
> +
> +  Private    = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +  RetryCount = 0;
> +  ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +  ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> +  Request.Content       = Content;
> +  Request.ContentLength = ContentSize;
> +  Request.ContentType   = ContentType;
> +
> +  //
> +  // Patch resource to redfish service.
> +  //
> +  do {
> +    RetryCount += 1;
> +    Status      = HttpSendReceive (
> +                    Service,
> +                    Uri,
> +                    HttpMethodDelete,
> +                    &Request,
> +                    Response
> +                    );
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> +    if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryDelete)) {
> +      break;
> +    }
> +
> +    //
> +    // Retry when BMC is not ready.
> +    //
> +    if ((Response->StatusCode != NULL)) {
> +      DEBUG_CODE (
> +        DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +        );
> +
> +      if (!RedfishRetryRequired (Response->StatusCode)) {
> +        break;
> +      }
> +
> +      //
> +      // Release response for next round of request.
> +      //
> +      This->FreeResponse (This, Response);
> +    }
> +
> +    DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry
> (%d/%d)\n", __func__, RetryCount, Private-
> >RetrySetting.MaximumRetryDelete));
> +    if (Private->RetrySetting.RetryWait > 0) {
> +      gBS->Stall (Private->RetrySetting.RetryWait);
> +    }
> +  } while (TRUE);
> +
> +  //
> +  // Redfish resource is updated. Automatically expire the cached response
> +  // so application can directly get resource from Redfish service again.
> +  //
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> +  RedfishExpireResponse (This, Uri);
> +
> +  if (EFI_ERROR (Status)) {
> +    DEBUG_CODE (
> +      DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> +      );
> +    //
> +    // Report status code for Redfish failure
> +    //
> +    ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode);
> +    DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status));
> +    goto ON_RELEASE;
> +  }
> +
> +ON_RELEASE:
> +
> +  return Status;
> +}
> +
> +EDKII_REDFISH_HTTP_PROTOCOL  mEdkIIRedfishHttpProtocol = {
> +  EDKII_REDFISH_HTTP_PROTOCOL_REVISION,
> +  RedfishCreateRedfishService,
> +  RedfishFreeRedfishService,
> +  RedfishJsonInRedfishPayload,
> +  RedfishGetResource,
> +  RedfishPatchResource,
> +  RedfishPutResource,
> +  RedfishPostResource,
> +  RedfishDeleteResource,
> +  RedfishFreeRequest,
> +  RedfishFreeResponse,
> +  RedfishExpireResponse
> +};
> +
> +/**
> +  Unloads an image.
> +
> +  @param[in]  ImageHandle         Handle that identifies the image to be
> unloaded.
> +
> +  @retval EFI_SUCCESS             The image has been unloaded.
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image
> handle.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpDriverUnload (
> +  IN EFI_HANDLE  ImageHandle
> +  )
> +{
> +  if (mRedfishHttpCachePrivate == NULL) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
> +    ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
> +  }
> +
> +  gBS->UninstallMultipleProtocolInterfaces (
> +         ImageHandle,
> +         &gEdkIIRedfishHttpProtocolGuid,
> +         &mRedfishHttpCachePrivate->Protocol,
> +         NULL
> +         );
> +
> +  FreePool (mRedfishHttpCachePrivate);
> +  mRedfishHttpCachePrivate = NULL;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler.
> +
> +  @param[in] Event    Event whose notification function is being invoked.
> +  @param[in] Context  Pointer to the notification function's context.
> +
> +**/
> +VOID
> +EFIAPI
> +CredentialProtocolInstalled (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  REDFISH_HTTP_CACHE_PRIVATE  *Private;
> +
> +  Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context;
> +  if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) {
> +    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> +    return;
> +  }
> +
> +  //
> +  // Locate HII database protocol.
> +  //
> +  Status = gBS->LocateProtocol (
> +                  &gEdkIIRedfishCredentialProtocolGuid,
> +                  NULL,
> +                  (VOID **)&Private->CredentialProtocol
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return;
> +  }
> +
> +  gBS->CloseEvent (Event);
> +}
> +
> +/**
> +  Main entry for this driver.
> +
> +  @param[in] ImageHandle     Image handle this driver.
> +  @param[in] SystemTable     Pointer to SystemTable.
> +
> +  @retval EFI_SUCCESS     This function always complete successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpEntryPoint (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  EFI_STATUS  Status;
> +  VOID        *Registration;
> +
> +  if (mRedfishHttpCachePrivate != NULL) {
> +    return EFI_ALREADY_STARTED;
> +  }
> +
> +  mRedfishHttpCachePrivate = AllocateZeroPool (sizeof
> (REDFISH_HTTP_CACHE_PRIVATE));
> +  if (mRedfishHttpCachePrivate == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // Initial cache list and protocol instance.
> +  //
> +  mRedfishHttpCachePrivate->Signature   =
> REDFISH_HTTP_DRIVER_SIGNATURE;
> +  mRedfishHttpCachePrivate->ImageHandle = ImageHandle;
> +  CopyMem (&mRedfishHttpCachePrivate->Protocol,
> &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL));
> +  mRedfishHttpCachePrivate->CacheList.Capacity =
> REDFISH_HTTP_CACHE_LIST_SIZE;
> +  mRedfishHttpCachePrivate->CacheList.Count    = 0x00;
> +  mRedfishHttpCachePrivate->CacheDisabled      = PcdGetBool
> (PcdHttpCacheDisabled);
> +  InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
> +
> +  //
> +  // Get retry settings
> +  //
> +  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet    = PcdGet16
> (PcdHttpGetRetry);
> +  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut    = PcdGet16
> (PcdHttpPutRetry);
> +  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch  = PcdGet16
> (PcdHttpPatchRetry);
> +  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost   = PcdGet16
> (PcdHttpPostRetry);
> +  mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16
> (PcdHttpDeleteRetry);
> +  mRedfishHttpCachePrivate->RetrySetting.RetryWait          = PcdGet16
> (PcdHttpRetryWaitInSecond) * 1000000U;
> +
> +  //
> +  // Install the gEdkIIRedfishHttpProtocolGuid onto Handle.
> +  //
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &mRedfishHttpCachePrivate->ImageHandle,
> +                  &gEdkIIRedfishHttpProtocolGuid,
> +                  &mRedfishHttpCachePrivate->Protocol,
> +                  NULL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n",
> __func__, Status));
> +    RedfishHttpDriverUnload (ImageHandle);
> +    return Status;
> +  }
> +
> +  //
> +  // Install protocol notification if credential protocol is installed.
> +  //
> +  mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent (
> +                                            &gEdkIIRedfishCredentialProtocolGuid,
> +                                            TPL_CALLBACK,
> +                                            CredentialProtocolInstalled,
> +                                            mRedfishHttpCachePrivate,
> +                                            &Registration
> +                                            );
> +  if (mRedfishHttpCachePrivate->NotifyEvent == NULL) {
> +    DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for
> gEdkIIRedfishCredentialProtocolGuid\n", __func__));
> +    ASSERT (FALSE);
> +    RedfishHttpDriverUnload (ImageHandle);
> +    return Status;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> new file mode 100644
> index 0000000000..5652818d16
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> @@ -0,0 +1,693 @@
> +/** @file
> +  RedfishHttpOperation handles HTTP operations.
> +
> +  Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpOperation.h"
> +#include "RedfishHttpData.h"
> +
> +/**
> +  This function copies all headers in SrcHeaders to DstHeaders.
> +  It's call responsibility to release returned DstHeaders.
> +
> +  @param[in]  SrcHeaders      Source headers.
> +  @param[in]  SrcHeaderCount  Number of header in source headers.
> +  @param[out] DstHeaders      Destination headers.
> +  @param[out] DstHeaderCount  Number of header in designation headers.
> +
> +  @retval     EFI_SUCCESS     Headers are copied successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +CopyHttpHeaders (
> +  IN  EFI_HTTP_HEADER  *SrcHeaders,
> +  IN  UINTN            SrcHeaderCount,
> +  OUT EFI_HTTP_HEADER  **DstHeaders,
> +  OUT UINTN            *DstHeaderCount
> +  )
> +{
> +  UINTN  Index;
> +
> +  if ((SrcHeaders == NULL) || (SrcHeaderCount == 0) || (DstHeaders == NULL)
> || (DstHeaderCount == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  *DstHeaderCount = 0;
> +  *DstHeaders     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> SrcHeaderCount);
> +  if (*DstHeaders == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  for (Index = 0; Index < SrcHeaderCount; Index++) {
> +    (*DstHeaders)[Index].FieldName = AllocateCopyPool (AsciiStrSize
> (SrcHeaders[Index].FieldName), SrcHeaders[Index].FieldName);
> +    if ((*DstHeaders)[Index].FieldName == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
> +    if ((*DstHeaders)[Index].FieldValue == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    *DstHeaderCount += 1;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function free resources in Request. Request is no longer available
> +  after this function returns successfully.
> +
> +  @param[in]  Request      HTTP request to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishRequest (
> +  IN  REDFISH_REQUEST  *Request
> +  )
> +{
> +  if (Request == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Request->Headers != NULL) && (Request->HeaderCount > 0)) {
> +    HttpFreeHeaderFields (Request->Headers, Request->HeaderCount);
> +    Request->Headers     = NULL;
> +    Request->HeaderCount = 0;
> +  }
> +
> +  if (Request->Content != NULL) {
> +    FreePool (Request->Content);
> +    Request->Content = NULL;
> +  }
> +
> +  if (Request->ContentType != NULL) {
> +    FreePool (Request->ContentType);
> +    Request->ContentType = NULL;
> +  }
> +
> +  Request->ContentLength = 0;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function free resources in given Response.
> +
> +  @param[in]  Response     HTTP response to be released.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishResponse (
> +  IN  REDFISH_RESPONSE  *Response
> +  )
> +{
> +  if (Response == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Response->Headers != NULL) && (Response->HeaderCount > 0)) {
> +    HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
> +    Response->Headers     = NULL;
> +    Response->HeaderCount = 0;
> +  }
> +
> +  if (Response->Payload != NULL) {
> +    ReleaseRedfishPayload (Response->Payload);
> +    Response->Payload = NULL;
> +  }
> +
> +  if (Response->StatusCode != NULL) {
> +    FreePool (Response->StatusCode);
> +    Response->StatusCode = NULL;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function free resources in given HTTP message.
> +
> +  @param[in]  HttpMessage     HTTP message to be released.
> +  @param[in]  IsRequest       TRUE if this is request type of HTTP message.
> +                              FALSE if this is response type of HTTP message.
> +
> +  @retval     EFI_SUCCESS     Resrouce is released successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseHttpMessage (
> +  IN  EFI_HTTP_MESSAGE  *HttpMessage,
> +  IN  BOOLEAN           IsRequest
> +  )
> +{
> +  if (HttpMessage == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (IsRequest) {
> +    if (HttpMessage->Data.Request != NULL) {
> +      if (HttpMessage->Data.Request->Url != NULL) {
> +        FreePool (HttpMessage->Data.Request->Url);
> +      }
> +
> +      FreePool (HttpMessage->Data.Request);
> +      HttpMessage->Data.Request = NULL;
> +    }
> +  } else {
> +    if (HttpMessage->Data.Response != NULL) {
> +      FreePool (HttpMessage->Data.Response);
> +      HttpMessage->Data.Response = NULL;
> +    }
> +  }
> +
> +  if (HttpMessage->Body != NULL) {
> +    FreePool (HttpMessage->Body);
> +    HttpMessage->Body = NULL;
> +  }
> +
> +  if (HttpMessage->Headers != NULL) {
> +    HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage-
> >HeaderCount);
> +    HttpMessage->Headers     = NULL;
> +    HttpMessage->HeaderCount = 0;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function build Redfish message for sending data to Redfish service.
> +  It's call responsibility to properly release returned HTTP message by
> +  calling ReleaseHttpMessage.
> +
> +  @param[in]   ServicePrivate    Pointer to Redfish service private data.
> +  @param[in]   Uri               Redfish service URI.
> +  @param[in]   Method            HTTP method.
> +  @param[in]   Request           Additional data to send to Redfish service.
> +                                 This is optional.
> +  @param[in]   ContentEncoding   Content encoding method to compress
> HTTP context.
> +                                 This is optional. When ContentEncoding is NULL,
> +                                 No compress method will be performed.
> +
> +  @retval     EFI_HTTP_MESSAGE *   Pointer to newly created HTTP message.
> +  @retval     NULL                 Error occurred.
> +
> +**/
> +EFI_HTTP_MESSAGE *
> +BuildRequestMessage (
> +  IN REDFISH_SERVICE_PRIVATE  *ServicePrivate,
> +  IN EFI_STRING               Uri,
> +  IN EFI_HTTP_METHOD          Method,
> +  IN REDFISH_REQUEST          *Request OPTIONAL,
> +  IN CHAR8                    *ContentEncoding OPTIONAL
> +  )
> +{
> +  EFI_STATUS             Status;
> +  EFI_STRING             Url;
> +  UINTN                  UrlSize;
> +  UINTN                  Index;
> +  EFI_HTTP_MESSAGE       *RequestMsg;
> +  EFI_HTTP_REQUEST_DATA  *RequestData;
> +  UINTN                  HeaderCount;
> +  UINTN                  HeaderIndex;
> +  EFI_HTTP_HEADER        *Headers;
> +  CHAR8                  ContentLengthStr[REDFISH_CONTENT_LENGTH_SIZE];
> +  VOID                   *Content;
> +  UINTN                  ContentLength;
> +  BOOLEAN                HasContent;
> +  BOOLEAN                DoContentEncoding;
> +
> +  RequestMsg        = NULL;
> +  RequestData       = NULL;
> +  Url               = NULL;
> +  UrlSize           = 0;
> +  Content           = NULL;
> +  ContentLength     = 0;
> +  HeaderCount       = REDFISH_COMMON_HEADER_SIZE;
> +  HeaderIndex       = 0;
> +  Headers           = NULL;
> +  HasContent        = FALSE;
> +  DoContentEncoding = FALSE;
> +
> +  if ((ServicePrivate == NULL) || (IS_EMPTY_STRING (Uri))) {
> +    return NULL;
> +  }
> +
> +  if (Method >= HttpMethodMax) {
> +    return NULL;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__,
> Uri));
> +
> +  //
> +  // Build full URL for HTTP query.
> +  //
> +  UrlSize = (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * sizeof
> (CHAR16);
> +  Url     = AllocateZeroPool (UrlSize);
> +  if (Url == NULL) {
> +    return NULL;
> +  }
> +
> +  UnicodeSPrint (Url, UrlSize, L"%a%s", ServicePrivate->Host, Uri);
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Url: %s\n",
> __func__, Url));
> +
> +  //
> +  // Step 1: build the HTTP headers.
> +  //
> +  if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)
> || !IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
> +    HeaderCount++;
> +  }
> +
> +  if ((Request != NULL) && (Request->HeaderCount > 0)) {
> +    HeaderCount += Request->HeaderCount;
> +  }
> +
> +  //
> +  // Check and see if we will do content encoding or not
> +  //
> +  if (!IS_EMPTY_STRING (ContentEncoding)) {
> +    if (AsciiStrCmp (ContentEncoding,
> REDFISH_HTTP_CONTENT_ENCODING_NONE) != 0) {
> +      DoContentEncoding = TRUE;
> +    }
> +  }
> +
> +  if ((Request != NULL) && !IS_EMPTY_STRING (Request->Content)) {
> +    HeaderCount += 2;
> +    HasContent   = TRUE;
> +    if (DoContentEncoding) {
> +      HeaderCount += 1;
> +    }
> +  }
> +
> +  Headers = AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER));
> +  if (Headers == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) {
> +    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_X_AUTH_TOKEN, ServicePrivate->SessionToken);
> +    if (EFI_ERROR (Status)) {
> +      goto ON_ERROR;
> +    }
> +  } else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
> +    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_AUTHORIZATION, ServicePrivate->BasicAuth);
> +    if (EFI_ERROR (Status)) {
> +      goto ON_ERROR;
> +    }
> +  }
> +
> +  if (Request != NULL) {
> +    for (Index = 0; Index < Request->HeaderCount; Index++) {
> +      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> Request->Headers[Index].FieldName, Request->Headers[Index].FieldValue);
> +      if (EFI_ERROR (Status)) {
> +        goto ON_ERROR;
> +      }
> +    }
> +  }
> +
> +  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_HOST, ServicePrivate->HostName);
> +  if (EFI_ERROR (Status)) {
> +    goto ON_ERROR;
> +  }
> +
> +  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> REDFISH_HTTP_HEADER_ODATA_VERSION_STR,
> REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE);
> +  if (EFI_ERROR (Status)) {
> +    goto ON_ERROR;
> +  }
> +
> +  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON);
> +  if (EFI_ERROR (Status)) {
> +    goto ON_ERROR;
> +  }
> +
> +  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_USER_AGENT,
> REDFISH_HTTP_HEADER_USER_AGENT_VALUE);
> +  if (EFI_ERROR (Status)) {
> +    goto ON_ERROR;
> +  }
> +
> +  Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> REDFISH_HTTP_HEADER_CONNECTION_STR,
> REDFISH_HTTP_HEADER_CONNECTION_VALUE);
> +  if (EFI_ERROR (Status)) {
> +    goto ON_ERROR;
> +  }
> +
> +  //
> +  // Handle content header
> +  //
> +  if (HasContent) {
> +    if (Request->ContentType == NULL) {
> +      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON);
> +      if (EFI_ERROR (Status)) {
> +        goto ON_ERROR;
> +      }
> +    } else {
> +      Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_CONTENT_TYPE, Request->ContentType);
> +      if (EFI_ERROR (Status)) {
> +        goto ON_ERROR;
> +      }
> +    }
> +
> +    if (Request->ContentLength == 0) {
> +      Request->ContentLength =  AsciiStrLen (Request->Content);
> +    }
> +
> +    AsciiSPrint (
> +      ContentLengthStr,
> +      sizeof (ContentLengthStr),
> +      "%lu",
> +      (UINT64)Request->ContentLength
> +      );
> +    Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_CONTENT_LENGTH, ContentLengthStr);
> +    if (EFI_ERROR (Status)) {
> +      goto ON_ERROR;
> +    }
> +
> +    //
> +    // Encoding
> +    //
> +    if (DoContentEncoding) {
> +      //
> +      // We currently only support gzip Content-Encoding.
> +      //
> +      Status =  RedfishContentEncode (
> +                  ContentEncoding,
> +                  Request->Content,
> +                  Request->ContentLength,
> +                  &Content,
> +                  &ContentLength
> +                  );
> +      if (Status == EFI_INVALID_PARAMETER) {
> +        DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__));
> +        goto ON_ERROR;
> +      } else if (Status == EFI_UNSUPPORTED) {
> +        DoContentEncoding = FALSE;
> +        DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content
> coding for %a! Use raw data instead.\n", __func__, ContentEncoding));
> +        Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_CONTENT_ENCODING,
> HTTP_CONTENT_ENCODING_IDENTITY);
> +        if (EFI_ERROR (Status)) {
> +          goto ON_ERROR;
> +        }
> +      } else {
> +        Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++],
> HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_GZIP);
> +        if (EFI_ERROR (Status)) {
> +          goto ON_ERROR;
> +        }
> +      }
> +    }
> +
> +    //
> +    // When the content is from caller, we use our own copy so that we
> properly release it later.
> +    //
> +    if (!DoContentEncoding) {
> +      Content = AllocateCopyPool (Request->ContentLength, Request-
> >Content);
> +      if (Content == NULL) {
> +        goto ON_ERROR;
> +      }
> +
> +      ContentLength = Request->ContentLength;
> +    }
> +  }
> +
> +  //
> +  // Step 2: build the rest of HTTP request info.
> +  //
> +  RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA));
> +  if (RequestData == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  RequestData->Method = Method;
> +  RequestData->Url    = Url;
> +
> +  //
> +  // Step 3: fill in EFI_HTTP_MESSAGE
> +  //
> +  RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE));
> +  if (RequestMsg == NULL) {
> +    goto ON_ERROR;
> +  }
> +
> +  ASSERT (HeaderIndex == HeaderCount);
> +  RequestMsg->Data.Request = RequestData;
> +  RequestMsg->HeaderCount  = HeaderIndex;
> +  RequestMsg->Headers      = Headers;
> +
> +  if (HasContent) {
> +    RequestMsg->BodyLength = ContentLength;
> +    RequestMsg->Body       = Content;
> +  }
> +
> +  return RequestMsg;
> +
> +ON_ERROR:
> +
> +  if (Headers != NULL) {
> +    HttpFreeHeaderFields (Headers, HeaderIndex);
> +  }
> +
> +  if (RequestData != NULL) {
> +    FreePool (RequestData);
> +  }
> +
> +  if (RequestMsg != NULL) {
> +    FreePool (RequestMsg);
> +  }
> +
> +  if (Url != NULL) {
> +    FreePool (Url);
> +  }
> +
> +  return NULL;
> +}
> +
> +/**
> +  This function parse response message from Redfish service, and
> +  build Redfish response for caller. It's call responsibility to
> +  properly release Redfish response by calling ReleaseRedfishResponse.
> +
> +  @param[in]   ServicePrivate   Pointer to Redfish service private data.
> +  @param[in]   ResponseMsg      Response message from Redfish service.
> +  @param[out]  RedfishResponse  Redfish response data.
> +
> +  @retval     EFI_SUCCESS     Redfish response is returned successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +ParseResponseMessage (
> +  IN  REDFISH_SERVICE_PRIVATE  *ServicePrivate,
> +  IN  EFI_HTTP_MESSAGE         *ResponseMsg,
> +  OUT REDFISH_RESPONSE         *RedfishResponse
> +  )
> +{
> +  EFI_STATUS        Status;
> +  EDKII_JSON_VALUE  JsonData;
> +  EFI_HTTP_HEADER   *ContentEncodedHeader;
> +  VOID              *DecodedBody;
> +  UINTN             DecodedLength;
> +
> +  if ((ServicePrivate == NULL) || (ResponseMsg == NULL) || (RedfishResponse
> == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__));
> +
> +  //
> +  // Initialization
> +  //
> +  JsonData                     = NULL;
> +  RedfishResponse->HeaderCount = 0;
> +  RedfishResponse->Headers     = NULL;
> +  RedfishResponse->Payload     = NULL;
> +  RedfishResponse->StatusCode  = NULL;
> +  DecodedBody                  = NULL;
> +  DecodedLength                = 0;
> +
> +  //
> +  // Return the HTTP StatusCode.
> +  //
> +  if (ResponseMsg->Data.Response != NULL) {
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n",
> __func__, ResponseMsg->Data.Response->StatusCode));
> +    RedfishResponse->StatusCode = AllocateCopyPool (sizeof
> (EFI_HTTP_STATUS_CODE), &ResponseMsg->Data.Response->StatusCode);
> +    if (RedfishResponse->StatusCode == NULL) {
> +      DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n",
> __func__));
> +    }
> +  }
> +
> +  //
> +  // Return the HTTP headers.
> +  //
> +  if (ResponseMsg->Headers != NULL) {
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count:
> %d\n", __func__, ResponseMsg->HeaderCount));
> +    Status = CopyHttpHeaders (
> +               ResponseMsg->Headers,
> +               ResponseMsg->HeaderCount,
> +               &RedfishResponse->Headers,
> +               &RedfishResponse->HeaderCount
> +               );
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_ERROR, "%a: Failed to copy HTTP headers: %r\n",
> __func__, Status));
> +    }
> +  }
> +
> +  //
> +  // Return the HTTP body.
> +  //
> +  if ((ResponseMsg->BodyLength != 0) && (ResponseMsg->Body != NULL)) {
> +    DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length:
> %d\n", __func__, ResponseMsg->BodyLength));
> +    //
> +    // Check if data is encoded.
> +    //
> +    ContentEncodedHeader = HttpFindHeader (RedfishResponse-
> >HeaderCount, RedfishResponse->Headers,
> HTTP_HEADER_CONTENT_ENCODING);
> +    if (ContentEncodedHeader != NULL) {
> +      //
> +      // The content is encoded.
> +      //
> +      Status = RedfishContentDecode (
> +                 ContentEncodedHeader->FieldValue,
> +                 ResponseMsg->Body,
> +                 ResponseMsg->BodyLength,
> +                 &DecodedBody,
> +                 &DecodedLength
> +                 );
> +      if (EFI_ERROR (Status)) {
> +        DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response
> content: %r decoding method: %a\n.", __func__, Status,
> ContentEncodedHeader->FieldValue));
> +        goto ON_ERROR;
> +      }
> +
> +      JsonData = JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL);
> +      FreePool (DecodedBody);
> +    } else {
> +      JsonData = JsonLoadBuffer (ResponseMsg->Body, ResponseMsg-
> >BodyLength, 0, NULL);
> +    }
> +
> +    if (!JsonValueIsNull (JsonData)) {
> +      RedfishResponse->Payload = CreateRedfishPayload (ServicePrivate,
> JsonData);
> +      if (RedfishResponse->Payload == NULL) {
> +        DEBUG ((DEBUG_ERROR, "%a: Failed to create payload\n.", __func__));
> +      }
> +
> +      JsonValueFree (JsonData);
> +    } else {
> +      DEBUG ((DEBUG_ERROR, "%a: No payload available\n", __func__));
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +
> +ON_ERROR:
> +
> +  if (RedfishResponse != NULL) {
> +    ReleaseRedfishResponse (RedfishResponse);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  This function send Redfish request to Redfish service by calling
> +  Rest Ex protocol.
> +
> +  @param[in]   Service       Pointer to Redfish service.
> +  @param[in]   Uri           Uri of Redfish service.
> +  @param[in]   Method        HTTP method.
> +  @param[in]   Request     Request data. This is optional.
> +  @param[out]  Response    Redfish response data.
> +
> +  @retval     EFI_SUCCESS     Request is sent and received successfully.
> +  @retval     Others          Errors occur.
> +
> +**/
> +EFI_STATUS
> +HttpSendReceive (
> +  IN  REDFISH_SERVICE   Service,
> +  IN  EFI_STRING        Uri,
> +  IN  EFI_HTTP_METHOD   Method,
> +  IN  REDFISH_REQUEST   *Request  OPTIONAL,
> +  OUT REDFISH_RESPONSE  *Response
> +  )
> +{
> +  EFI_STATUS               Status;
> +  EFI_STATUS               RestExStatus;
> +  EFI_HTTP_MESSAGE         *RequestMsg;
> +  EFI_HTTP_MESSAGE         ResponseMsg;
> +  REDFISH_SERVICE_PRIVATE  *ServicePrivate;
> +  EFI_HTTP_HEADER          *XAuthTokenHeader;
> +  CHAR8                    *HttpContentEncoding;
> +
> +  if ((Service == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x
> %s\n", __func__, Method, Uri));
> +
> +  ServicePrivate = (REDFISH_SERVICE_PRIVATE *)Service;
> +  if (ServicePrivate->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
> +    DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  ZeroMem (&ResponseMsg, sizeof (ResponseMsg));
> +  HttpContentEncoding = (CHAR8 *)PcdGetPtr
> (PcdRedfishServiceContentEncoding);
> +
> +  RequestMsg = BuildRequestMessage (Service, Uri, Method, Request,
> HttpContentEncoding);
> +  if (RequestMsg == NULL) {
> +    DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n",
> __func__, Uri));
> +    return EFI_PROTOCOL_ERROR;
> +  }
> +
> +  //
> +  // call RESTEx to get response from REST service.
> +  //
> +  RestExStatus = ServicePrivate->RestEx->SendReceive (ServicePrivate-
> >RestEx, RequestMsg, &ResponseMsg);
> +  if (EFI_ERROR (RestExStatus)) {
> +    DEBUG ((DEBUG_ERROR, "%a: %s SendReceive failure: %r\n", __func__,
> Uri, RestExStatus));
> +  }
> +
> +  //
> +  // Return status code, headers and payload to caller as much as possible
> even when RestEx returns failure.
> +  //
> +  Status = ParseResponseMessage (ServicePrivate, &ResponseMsg,
> Response);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "%a: %s parse response failure: %r\n", __func__,
> Uri, Status));
> +  } else {
> +    //
> +    // Capture session token in header
> +    //
> +    if ((Method == HttpMethodPost) &&
> +        (Response->StatusCode != NULL) &&
> +        ((*Response->StatusCode == HTTP_STATUS_200_OK) || (*Response-
> >StatusCode == HTTP_STATUS_204_NO_CONTENT)))
> +    {
> +      XAuthTokenHeader = HttpFindHeader (ResponseMsg.HeaderCount,
> ResponseMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN);
> +      if (XAuthTokenHeader != NULL) {
> +        Status = UpdateSessionToken (ServicePrivate, XAuthTokenHeader-
> >FieldValue);
> +        if (EFI_ERROR (Status)) {
> +          DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n",
> __func__, Status));
> +        }
> +      }
> +    }
> +  }
> +
> +  //
> +  // Release resources
> +  //
> +  if (RequestMsg != NULL) {
> +    ReleaseHttpMessage (RequestMsg, TRUE);
> +    FreePool (RequestMsg);
> +  }
> +
> +  ReleaseHttpMessage (&ResponseMsg, FALSE);
> +
> +  return RestExStatus;
> +}
> diff --git a/RedfishPkg/Redfish.fdf.inc b/RedfishPkg/Redfish.fdf.inc
> index 3e5a77766e..5cbe3592fd 100644
> --- a/RedfishPkg/Redfish.fdf.inc
> +++ b/RedfishPkg/Redfish.fdf.inc
> @@ -6,7 +6,7 @@
>  # to be built in the firmware volume.
>  #
>  # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
>  #
>  #    SPDX-License-Identifier: BSD-2-Clause-Patent
>  #
> @@ -20,4 +20,5 @@
>    INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
>    INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
>    INF
> MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> +  INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
>  !endif
> --
> 2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115692): https://edk2.groups.io/g/devel/message/115692
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
  2024-02-21  1:51 ` Chang, Abner via groups.io
@ 2024-02-22  9:16   ` Nickle Wang via groups.io
  0 siblings, 0 replies; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-22  9:16 UTC (permalink / raw)
  To: Chang, Abner, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez

Thanks for catching this typo, Abner. Version 2 patch is sent.

Regards,
Nickle

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



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115781): https://edk2.groups.io/g/devel/message/115781
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2024-02-22  9:16 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-20  6:41 [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
2024-02-21  1:51 ` Chang, Abner via groups.io
2024-02-22  9:16   ` Nickle Wang via groups.io

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox