summaryrefslogtreecommitdiffstats
path: root/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
diff options
context:
space:
mode:
Diffstat (limited to 'RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c')
-rw-r--r--RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c1344
1 files changed, 1344 insertions, 0 deletions
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
new file mode 100644
index 0000000000..8dcdf55c62
--- /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 Resource 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 Resource 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 Resource 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 Resource 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 Resource 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 Resource 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 Resource 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 Resource 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;
+}