From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by spool.mail.gandi.net (Postfix) with ESMTPS id 0991AD8030B for ; Thu, 22 Feb 2024 14:18:03 +0000 (UTC) DKIM-Signature: a=rsa-sha256; bh=ZZVP8wJEmc6H1a83iPnz6XenkovwexNQxJafwM25j4k=; c=relaxed/simple; d=groups.io; h=ARC-Seal:ARC-Message-Signature:ARC-Authentication-Results:From:To:CC:Subject:Thread-Topic:Thread-Index:Date:Message-ID:References:In-Reply-To:Accept-Language:MIME-Version:Precedence:List-Subscribe:List-Help:Sender:List-Id:Mailing-List:Delivered-To:Reply-To:List-Unsubscribe-Post:List-Unsubscribe:Content-Language:Content-Type:Content-Transfer-Encoding; s=20140610; t=1708611482; v=1; b=DRs3Dhiko7YA3SaNsUu6yrRenia7cmjHt8L/FoXhoqQ1zY+WeHrv+YksIDi80u/E3T+6JzvC UbhVbsautxUwTOgiKuDaS1kaSED4KQds0CBxSXZBtzUuEM4yHnYD0ML7iV47fiWdj9Daxi7Y18Q Chxwvm0/rvbn+W0Ci08t3cQE= X-Received: by 127.0.0.2 with SMTP id 6rXuYY7687511xh8LMmjVPNK; Thu, 22 Feb 2024 06:18:02 -0800 X-Received: from NAM12-BN8-obe.outbound.protection.outlook.com (NAM12-BN8-obe.outbound.protection.outlook.com [40.107.237.90]) by mx.groups.io with SMTP id smtpd.web11.13647.1708611481755071843 for ; Thu, 22 Feb 2024 06:18:02 -0800 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=nNu03B625B3f+3CTgzUhZngtKXXeUJHgkXhNF0VOsjVgcYaoTs8yZhgVFw5TaZ7IUbzaEjfrO1NRN229BMT0NXgSXbQoTqNyJLC1ZwENmc3eOKr6WgpRBhfnZHIHKq1t2iPUC5Pmwtc10v/CEasM+J7g02gyyTnoZFkD7yfhr57yXutYqkPPaEJSeZeoCVHdrgmzvnIWDdgQ5F8OMxG+axvKlXlaqfr3usG6LHe+ae+3HKhhDTpmONh+JGdmqhI3CfegRbdxhVdreXm4ubfJaYNBuPgfWGi8FqAYpNUTYDkb5FX8E9m4EjxtUxxIfhniNEaU9EnJhVYTudcbQPwLDA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=GeipBg74Us1aNOwLenbBZD2mRCqnLFQpwPgg4uw8ed8=; b=aGztLL1Mt7TW6r79UcIqm6HVkuFwkuVqbXhNVS1hm0vF5/4+6zKvu7zdAlx8o5L/eo/7PcO58ywXVm1YLWNJglQj6NXWTcCF6ZF1IT6PEclX8dyH9jjSS8/KpNgMHUM/DiW+HgMs6U0qqKW2tQWRY18gRMiVnb0mbYrdYpDPFhk2tb9w1LwutZQaSk6PMWN+StuQYQG1fSEmdFyLIQqn/iw/D9FzAF5KyXZLb0C2Wvoo0v9bi+1AR3ShGryQk9ZD8U8OYQ+iasZw0UWxv+xZLok83U2BlrABXN1PkGHI4Q+tF3v0Fv+M1V55iEeNJpq3dzGw3LTwDbWSofHb1fVq7g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=ami.com; dmarc=pass action=none header.from=ami.com; dkim=pass header.d=ami.com; arc=none X-Received: from BLAPR10MB5185.namprd10.prod.outlook.com (2603:10b6:208:328::16) by SA1PR10MB7709.namprd10.prod.outlook.com (2603:10b6:806:3a9::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7316.24; Thu, 22 Feb 2024 14:17:57 +0000 X-Received: from BLAPR10MB5185.namprd10.prod.outlook.com ([fe80::7840:c724:8a2a:4cf5]) by BLAPR10MB5185.namprd10.prod.outlook.com ([fe80::7840:c724:8a2a:4cf5%5]) with mapi id 15.20.7316.023; Thu, 22 Feb 2024 14:17:57 +0000 From: "Igor Kulchytskyy via groups.io" To: Nickle Wang , "devel@edk2.groups.io" CC: Abner Chang , Nick Ramirez Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Thread-Topic: [EXTERNAL] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Thread-Index: AQHaZW8tTRPrfutKUEqPK7mViZKbz7EWaPqg Date: Thu, 22 Feb 2024 14:17:57 +0000 Message-ID: References: <20240222091122.644-1-nicklew@nvidia.com> In-Reply-To: <20240222091122.644-1-nicklew@nvidia.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-publictraffictype: Email x-ms-traffictypediagnostic: BLAPR10MB5185:EE_|SA1PR10MB7709:EE_ x-ms-office365-filtering-correlation-id: 4883a464-b014-4a19-585f-08dc33b11198 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam-message-info: BHY7nUM2iLLDcjIyipX7e/K3Ww1Z+/XCQTO+PbAeFW7yNobcz0TztxEX2d6zQN15iDPIiKcO05qsGg5GQfDMdfsiPaxKFheS6aD03ntFaaUs4IjFcpYQ1RradpDx4xJ1mj/hhp30TqiPT2eTITIvfLTGIjln3TFHY8NYU+QeKK9HYiATwTs92Lwhb6sJZGB9lmCIlVjvOZFf9j2jXnK0fuJTcmfkBDRbguZeLmaNlv6C5IDgq9Uf0/b7m+pwuCHnxEESBCYRg5NQHMt5oSzJLAfzCfeCNWOXoFgOUiIYMnyw8qpVBvP+Zh0mLIh6l8tmoQ42DSESi9Dl0fkHFcenIAY57xOTK53MspQ5y/BztpAweTQC4s+OVCgXrfl9cIgDjLvyMQJtxtSEx3Ctp6L704Qg5ECZtcMlwYgWkVBmM3HV55rioFQNOibkCuwxJPNB2TL+xjImKoj3FDWOf56pm+/819TxeOFZbsxLZ5/+7luIdX8cFE99iac0rxXmxXPZJPB1PbzgZ8NwzAptCwPGLecIl4pqbtxSTcbpNbj/Mf9gDQMpGqsJZh9nTmHuYenwczpOf9kDSUo0ayLoxdogwfZPImWR5UAEGhA1zXyaz+M= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?Z980jugfLzfPS3LItzNOL9/Ia5k46jJi4yiLbH9JMlrkXKpLW/r9q3PWKE3W?= =?us-ascii?Q?kxu/U2FaXn2auwPmXtBxjSAPLJIWzDCX+BvnJl7gM5yTkw32puLAhZ6MXxyn?= =?us-ascii?Q?+cb5545x53l2LW9YxqUUITzvwHVml1PWRlFZb+XPjj/qCNg9ZJBMET1Bfmav?= =?us-ascii?Q?mxW6OR60SDMTiJ3iRNvie3XsllLpaPgMKryw9ajQGQGLO2Fmf3FVaYFG9hVR?= =?us-ascii?Q?osnw89PvFYT0hh6prhVOVUaERxynNRG2GSSyYhmTFq7wg9JTk6+LZnV40rsS?= =?us-ascii?Q?Ez2TnXCJ7VJC7LmnL6dRZhHPbr/e2l971NBMUGV88YUETeTye4aBgjW2IGvN?= =?us-ascii?Q?3I0taUx4f40nYy89Hb3t9wLsYUWwjb6pxgC3RktKsHcJo9erAhNy9NenQpgz?= =?us-ascii?Q?TyMKkMgGDYPxrghRR6IQ/82QB+zAarlGVFZVx4fJyA0l9SCDecD9TLb6vQLo?= =?us-ascii?Q?W2eGOOfFrdBsjjvAx5jYSgnlWUxi35LPov9Ovz4RZxEdZX/5n3+U4VQvC8as?= =?us-ascii?Q?pT3VKVFmdVWJsxvnkLlgKrI/3qee1AgqnkzfqiSzuRM93nx5u+tRsUn3T+BJ?= =?us-ascii?Q?FXg5jRalUjuICmjfIebStz1o9shXGLyppxwRqcFzZowMwM3jed8dbcM9OLOQ?= =?us-ascii?Q?FgLvieSwP/dK+M83kav9eKtdM9peDNbimE99JB020CqzWr6Q4oo66e1mglSQ?= =?us-ascii?Q?GbUSPeYvgGF48hT0ZVIciovpVOUO1KnHLhxj4AdmgceVixHsxBJxWZ7O5sf3?= =?us-ascii?Q?nF7I1NBHnFZn1bYaL5KjGXNIhWDvsnnJeSANHyND8Knjqc2H2alzJWLP/NFq?= =?us-ascii?Q?MF35sRwboEsZi5HYTu2kNLmGx+g178RC/Zl+HmqzUf2aon0EvpU0BlO664V/?= =?us-ascii?Q?TlA0IVp+kMAwELW3aQXRzUuZSbcvZ7nPfhZLmwBdSQYlYD2QWPEOtnV6G3MS?= =?us-ascii?Q?gOGK5vK8HyCpU2W429/TySRIoPWxcFOUoa6pms7plKhh9WnSfBQoqpHthE9M?= =?us-ascii?Q?3n+tpn03+m8DmIyJde9YK9BbGe2g2HhsumipzZ4D/r5OBMQIntYt48IzTPeW?= =?us-ascii?Q?jO1G9X6ZKQ2qZ3vOwjFu68SbY1swIsXmHIO9IU57vJl1rdWn3Q1XUB5IDc0Z?= =?us-ascii?Q?ZC99WAwFuo5jlponmNNB11loCFehkGgaNIRTvAJ09R4pqjdOsZX0oLftJuo/?= =?us-ascii?Q?2mn8TRrcyFUrhIMoq79MODKxpJIWgH7p+xcg4aIrrTCC17yJJG3UFZLxo8TW?= =?us-ascii?Q?rE/eHkx257SWTxWlDa2EsRZU4fTeaxnwQ0vG5l5aGeCtBjhVYVYhmd2KocPP?= =?us-ascii?Q?gI0yu/uWyC3gSJqHHSUCxKt1QMIfNTxjHq2lDwDAv0F+E9qAhBgulGOEVQQr?= =?us-ascii?Q?ZF8iyLNTaEPEsKyOHcj3eQhnfUwLi4WqG6i7C+AmsWdsYScl7Wjct75nHJEM?= =?us-ascii?Q?oRB1uWkhqbKmqPWjIBNSjds16dFDC2xnblYhJgNJtsrc7cE6d4gADCERhVLw?= =?us-ascii?Q?o/DHmdn+QujSpoSj2Ha7FZgi7zgBW3fTWAtSjD9ynTnWfGIHm0HdzHd8R/cQ?= =?us-ascii?Q?/B1NRKgssspNS+ARLS8=3D?= MIME-Version: 1.0 X-OriginatorOrg: ami.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: BLAPR10MB5185.namprd10.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4883a464-b014-4a19-585f-08dc33b11198 X-MS-Exchange-CrossTenant-originalarrivaltime: 22 Feb 2024 14:17:57.6686 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 27e97857-e15f-486c-b58e-86c2b3040f93 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: UXEtiV42cdtmem2FqS8k72u3WvANQ/AkfRP1VYYMhq5BjeBRi/NbMQyOaQuCdkJr X-MS-Exchange-Transport-CrossTenantHeadersStamped: SA1PR10MB7709 Precedence: Bulk List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,igork@ami.com List-Unsubscribe-Post: List-Unsubscribe=One-Click List-Unsubscribe: X-Gm-Message-State: H9CBSEn63QfwfCL6fYrFxsdWx7686176AA= Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable X-GND-Status: LEGIT Authentication-Results: spool.mail.gandi.net; dkim=pass header.d=groups.io header.s=20140610 header.b=DRs3Dhik; spf=pass (spool.mail.gandi.net: domain of bounce@groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce@groups.io; dmarc=pass (policy=none) header.from=groups.io; arc=reject ("signature check failed: fail, {[1] = sig:microsoft.com:reject}") Reviewed-by: Igor Kulchytskyy -----Original Message----- From: Nickle Wang Sent: Thursday, February 22, 2024 4:11 AM To: devel@edk2.groups.io Cc: Igor Kulchytskyy ; Abner Chang ; Ni= ck Ramirez Subject: [EXTERNAL] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP proto= col **CAUTION: The e-mail below is from an external source. Please exercise cau= tion before opening attachments, clicking links, or following guidance.** implement Redfish HTTP protocol driver. Signed-off-by: Nickle Wang Co-authored-by: Igor Kulchytskyy Cc: Abner Chang Cc: Igor Kulchytskyy Cc: Nick Ramirez --- 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|0x= 00001009 - ## This is used to enable HTTP content encoding on Redfish communication= . - gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLE= AN|0x0000100A + # + # This PCD string is introduced for platform developer to set the encodi= ng method supported by BMC Redfish. + # Currently only "None" and "gzip" are supported. + # + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|VOI= D*|0x0000100A # # Use below PCDs to control Redfhs HTTP protocol. # diff --git a/RedfishPkg/RedfishComponents.dsc.inc b/RedfishPkg/RedfishCompo= nents.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
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved= . +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. # # 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/UefiHiiServic= esLib.inf RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNul= l/PlatformCredentialLibNull.inf RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/R= edfishContentCodingLibNull.inf + ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeRepor= tStatusCodeLib.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/Redf= ishHttpDxe/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 re= served. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x0001000b + BASE_NAME =3D RedfishHttpDxe + FILE_GUID =3D 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0 + MODULE_TYPE =3D DXE_DRIVER + VERSION_STRING =3D 1.0 + ENTRY_POINT =3D RedfishHttpEntryPoint + UNLOAD_IMAGE =3D RedfishHttpDriverUnload + +# +# VALID_ARCHITECTURES =3D 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/Redfi= shHttpDxe/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 res= erved. + + 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, L= ist, 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/Redfis= hHttpDxe/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 res= erved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EDKII_REDFISH_HTTP_DXE_H_ +#define EDKII_REDFISH_HTTP_DXE_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IS_EMPTY_STRING(a) ((a) =3D=3D NULL || (a)[0] =3D=3D '\0') +#define REDFISH_HTTP_CACHE_LIST_SIZE 0x80 +#define REDFISH_ERROR_MSG_MAX 128 +#define REDFISH_DEBUG_STRING_LENGTH 200 +#define REDFISH_HOST_NAME_MAX 64 // IPv6 maximum length (39)= + "https://" (8) + port number (maximum 5) +#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP %a failure(0x%x): = %s" +#define REDFISH_HTTP_CACHE_DEBUG DEBUG_MANAGEABILITY +#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_MANAGEABILITY +#define REDFISH_HTTP_CACHE_DEBUG_REQUEST DEBUG_MANAGEABILITY + +#endif diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h b/RedfishPkg/= RedfishHttpDxe/RedfishHttpOperation.h new file mode 100644 index 0000000000..d2f7cf4c27 --- /dev/null +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h @@ -0,0 +1,76 @@ +/** @file + Definitions of RedfishHttpOperation + + Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved= . + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EDKII_REDFISH_HTTP_OPERATION_H_ +#define EDKII_REDFISH_HTTP_OPERATION_H_ + +#include "RedfishHttpDxe.h" + +#define REDFISH_CONTENT_LENGTH_SIZE 80 +#define REDFISH_COMMON_HEADER_SIZE 5 +#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR "OData-Version" +#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE "4.0" +#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE "edk2redfish" +#define REDFISH_HTTP_HEADER_CONNECTION_STR "Connection" +#define REDFISH_HTTP_HEADER_CONNECTION_VALUE "Keep-Alive" +#define REDFISH_HTTP_CONTENT_ENCODING_NONE "None" + +/** + This function free resources in Request. Request is no longer available + after this function returns successfully. + + @param[in] Request HTTP request to be released. + + @retval EFI_SUCCESS Resrouce is released successfully. + @retval Others Errors occur. + +**/ +EFI_STATUS +ReleaseRedfishRequest ( + IN REDFISH_REQUEST *Request + ); + +/** + This function free resources in given Response. + + @param[in] Response HTTP response to be released. + + @retval EFI_SUCCESS Resrouce is released successfully. + @retval Others Errors occur. + +**/ +EFI_STATUS +ReleaseRedfishResponse ( + IN REDFISH_RESPONSE *Response + ); + +/** + This function send Redfish request to Redfish service by calling + Rest Ex protocol. + + @param[in] Service Pointer to Redfish service. + @param[in] Uri Uri of Redfish service. + @param[in] Method HTTP method. + @param[in] Request Request data. This is optional. + @param[out] Response Redfish response data. + + @retval EFI_SUCCESS Request is sent and received successfully. + @retval Others Errors occur. + +**/ +EFI_STATUS +HttpSendReceive ( + IN REDFISH_SERVICE Service, + IN EFI_STRING Uri, + IN EFI_HTTP_METHOD Method, + IN REDFISH_REQUEST *Request OPTIONAL, + OUT REDFISH_RESPONSE *Response + ); + +#endif diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c b/RedfishPkg/Redfi= shHttpDxe/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 res= erved. + + 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 =3D=3D NULL) || IS_EMPTY_STRING (Token)) { + return EFI_INVALID_PARAMETER; + } + + if (Service->SessionToken !=3D NULL) { + FreePool (Service->SessionToken); + } + + Service->SessionToken =3D AllocateCopyPool (AsciiStrSize (Token), Token)= ; + if (Service->SessionToken =3D=3D 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Service->Host !=3D NULL) { + FreePool (Service->Host); + } + + if (Service->HostName !=3D NULL) { + FreePool (Service->HostName); + } + + if (Service->BasicAuth !=3D NULL) { + ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth)); + FreePool (Service->BasicAuth); + } + + if (Service->SessionToken !=3D 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 =3D= =3D NULL)) { + return NULL; + } + + NewService =3D AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE)); + if (NewService =3D=3D NULL) { + return NULL; + } + + NewService->Signature =3D REDFISH_HTTP_SERVICE_SIGNATURE; + NewService->Host =3D AllocateCopyPool (AsciiStrSize (Host), Host); + if (NewService->Host =3D=3D NULL) { + goto ON_ERROR; + } + + NewService->HostName =3D AllocateCopyPool (AsciiStrSize (HostName), Host= Name); + if (NewService->HostName =3D=3D NULL) { + goto ON_ERROR; + } + + if (!IS_EMPTY_STRING (BasicAuth)) { + AuthStrSize =3D AsciiStrSize (BasicAuth) + AsciiStrLen (REDF= ISH_HTTP_BASIC_AUTH_STR); + NewService->BasicAuth =3D AllocateZeroPool (AuthStrSize); + if (NewService->BasicAuth =3D=3D NULL) { + goto ON_ERROR; + } + + AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a", REDFISH_HTTP_= BASIC_AUTH_STR, BasicAuth); + } + + if (!IS_EMPTY_STRING (SessionToken)) { + NewService->SessionToken =3D AllocateCopyPool (AsciiStrSize (SessionTo= ken), SessionToken); + if (NewService->SessionToken =3D=3D NULL) { + goto ON_ERROR; + } + } + + NewService->RestEx =3D 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Payload->Service !=3D NULL) { + ReleaseRedfishService (Payload->Service); + } + + if (Payload->JsonValue !=3D 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 =3D=3D NULL) || (JsonValue =3D=3D NULL)) { + return NULL; + } + + NewPayload =3D AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE)); + if (NewPayload =3D=3D NULL) { + return NULL; + } + + NewPayload->Signature =3D REDFISH_HTTP_PAYLOAD_SIGNATURE; + NewPayload->Service =3D CreateRedfishService (Service->Host, Service->= HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx); + if (NewPayload->Service =3D=3D NULL) { + goto ON_ERROR; + } + + NewPayload->JsonValue =3D JsonValueClone (JsonValue); + if (NewPayload->JsonValue =3D=3D 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 =3D=3D NULL) || (DstResponse =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (SrcResponse =3D=3D DstResponse) { + return EFI_SUCCESS; + } + + // + // Status code + // + if (SrcResponse->StatusCode !=3D NULL) { + DstResponse->StatusCode =3D AllocateCopyPool (sizeof (EFI_HTTP_STATUS_= CODE), SrcResponse->StatusCode); + if (DstResponse->StatusCode =3D=3D NULL) { + goto ON_ERROR; + } + } + + // + // Header + // + if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers !=3D NULL)) = { + DstResponse->HeaderCount =3D 0; + DstResponse->Headers =3D AllocateZeroPool (sizeof (EFI_HTTP_HEADER= ) * SrcResponse->HeaderCount); + if (DstResponse->Headers =3D=3D NULL) { + goto ON_ERROR; + } + + for (Index =3D 0; Index < SrcResponse->HeaderCount; Index++) { + DstResponse->Headers[Index].FieldName =3D AllocateCopyPool (AsciiStr= Size (SrcResponse->Headers[Index].FieldName), SrcResponse->Headers[Index].F= ieldName); + if (DstResponse->Headers[Index].FieldName =3D=3D NULL) { + goto ON_ERROR; + } + + DstResponse->Headers[Index].FieldValue =3D AllocateCopyPool (AsciiSt= rSize (SrcResponse->Headers[Index].FieldValue), SrcResponse->Headers[Index]= .FieldValue); + if (DstResponse->Headers[Index].FieldValue =3D=3D NULL) { + goto ON_ERROR; + } + + DstResponse->HeaderCount +=3D 1; + } + } + + // + // Payload + // + if (SrcResponse->Payload !=3D NULL) { + Payload =3D (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload; + if (Payload->Signature !=3D REDFISH_HTTP_PAYLOAD_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); + goto ON_ERROR; + } + + DstResponse->Payload =3D CreateRedfishPayload (Payload->Service, Paylo= ad->JsonValue); + if (DstResponse->Payload =3D=3D 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 =3D=3D NULL) { + return NULL; + } + + NewResponse =3D AllocateZeroPool (sizeof (REDFISH_RESPONSE)); + if (NewResponse =3D=3D NULL) { + return NULL; + } + + Status =3D 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 succ= essfully. + @retval EFI_INVALID_PARAMETER Data is NULL + +**/ +EFI_STATUS +ReleaseHttpCacheData ( + IN REDFISH_HTTP_CACHE_DATA *Data + ) +{ + if (Data =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Data->Uri !=3D NULL) { + FreePool (Data->Uri); + } + + if (Data->Response !=3D 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 =3D=3D NULL)) { + return NULL; + } + + NewData =3D AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA)); + if (NewData =3D=3D NULL) { + return NULL; + } + + NewData->Signature =3D REDFISH_HTTP_CACHE_SIGNATURE; + Size =3D StrSize (Uri); + NewData->Uri =3D AllocateCopyPool (Size, Uri); + if (NewData->Uri =3D=3D NULL) { + goto ON_ERROR; + } + + NewData->Response =3D Response; + NewData->HitCount =3D 1; + + return NewData; + +ON_ERROR: + + if (NewData !=3D 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 =3D NULL; + List =3D GetFirstNode (ListHeader); + while (!IsNull (ListHeader, List)) { + Data =3D REDFISH_HTTP_CACHE_FROM_LIST (List); + + if (StrCmp (Data->Uri, Uri) =3D=3D 0) { + return Data; + } + + List =3D 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 =3D NULL; + UnusedData =3D NULL; + HitCount =3D 0; + + List =3D GetFirstNode (ListHeader); + Data =3D REDFISH_HTTP_CACHE_FROM_LIST (List); + UnusedData =3D Data; + HitCount =3D Data->HitCount; + List =3D GetNextNode (ListHeader, List); + + while (!IsNull (ListHeader, List)) { + Data =3D REDFISH_HTTP_CACHE_FROM_LIST (List); + + if (Data->HitCount < HitCount) { + HitCount =3D Data->HitCount; + UnusedData =3D Data; + } + + List =3D 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 =3D=3D NULL) || (Data =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__, Data->Ur= i)); + + 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 =3D=3D NULL) || IS_EMPTY_STRING (Uri) || (Response =3D=3D NULL= )) { + return EFI_INVALID_PARAMETER; + } + + // + // If same cache data exist, replace it with latest one. + // + OldData =3D FindHttpCacheData (&List->Head, Uri); + if (OldData !=3D NULL) { + DeleteHttpCacheData (List, OldData); + } + + // + // Check capacity + // + if (List->Count >=3D List->Capacity) { + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused = cache\n", __func__)); + UnusedData =3D FindUnusedHttpCacheData (&List->Head); + if (UnusedData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DeleteHttpCacheData (List, UnusedData); + } + + // + // Clone a local copy + // + NewResponse =3D CloneRedfishResponse (Response); + if (NewResponse =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewData =3D NewHttpCacheData (Uri, NewResponse); + if (NewData =3D=3D 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__, Lis= t->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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsListEmpty (&CacheList->Head)) { + return EFI_SUCCESS; + } + + Data =3D NULL; + Next =3D NULL; + List =3D GetFirstNode (&CacheList->Head); + while (!IsNull (&CacheList->Head, List)) { + Data =3D REDFISH_HTTP_CACHE_FROM_LIST (List); + Next =3D GetNextNode (&CacheList->Head, List); + + DeleteHttpCacheData (CacheList, Data); + + List =3D Next; + } + + return EFI_SUCCESS; +} diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c b/RedfishPkg/Redfis= hHttpDxe/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 res= erved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RedfishHttpDxe.h" +#include "RedfishHttpData.h" +#include "RedfishHttpOperation.h" + +REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate =3D 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 =3D=3D 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, C= acheList->Capacity)); + Data =3D NULL; + Index =3D 0; + List =3D GetFirstNode (&CacheList->Head); + while (!IsNull (&CacheList->Head, List)) { + Data =3D REDFISH_HTTP_CACHE_FROM_LIST (List); + + DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data-= >HitCount)); + + List =3D 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 req= uest. + +**/ +BOOLEAN +RedfishRetryRequired ( + IN EFI_HTTP_STATUS_CODE *StatusCode + ) +{ + if (StatusCode =3D=3D NULL) { + return TRUE; + } + + if ((*StatusCode =3D=3D HTTP_STATUS_500_INTERNAL_SERVER_ERROR) || + (*StatusCode =3D=3D HTTP_STATUS_UNSUPPORTED_STATUS)) + { + return TRUE; + } + + return FALSE; +} + +/** + + Convert Unicode string to ASCII string. It's call responsibility to rele= ase 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 =3D StrLen (UnicodeStr) + 1; + AsciiStr =3D AllocateZeroPool (AsciiStrSize); + if (AsciiStr =3D=3D NULL) { + return NULL; + } + + Status =3D 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, Htt= pMethodToString (Method), (HttpStatusCode =3D=3D NULL ? HTTP_STATUS_UNSUPPO= RTED_STATUS : *HttpStatusCode), Uri); + DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg)); + // + // TODO: + // Below PI status code is approved by PIWG and wait for specification p= ublished. + // We will uncomment below report status code after PI status code get p= ublished. + // REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4483 + // + // REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + // EFI_ERROR_CODE | EFI_ERROR_MAJOR, + // EFI_COMPUTING_UNIT_MANAGEABILITY | EFI_MANAGEABILITY_EC_REDFISH_COMM= UNICATION_ERROR, + // ErrorMsg, + // AsciiStrSize (ErrorMsg) + // ); +} + +/** + This function create Redfish service. It's caller's responsibility to fr= ee returned + Redfish service by calling FreeService (). + + @param[in] This Pointer to EDKII_REDFISH_HTTP_PRO= TOCOL instance. + @param[in] RedfishConfigServiceInfo Redfish config service informatio= n. + + @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 =3D=3D NULL) || (RedfishConfigServiceInfo =3D=3D NULL)) { + return NULL; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n", __func__= , RedfishConfigServiceInfo->RedfishServiceLocation)); + + Private =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + BasicAuthString =3D NULL; + EncodedAuthString =3D NULL; + Username =3D NULL; + Password =3D NULL; + NewService =3D NULL; + AsciiLocation =3D NULL; + Host =3D NULL; + BasicAuthStrSize =3D 0; + EncodedAuthStrSize =3D 0; + UsernameSize =3D 0; + PasswordSize =3D 0; + + // + // Build host and host name from service location + // + if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation))= { + AsciiLocation =3D StringUnicodeToAscii (RedfishConfigServiceInfo->Redf= ishServiceLocation); + if (AsciiLocation =3D=3D NULL) { + goto ON_RELEASE; + } + + Host =3D AllocateZeroPool (REDFISH_HOST_NAME_MAX); + if (AsciiLocation =3D=3D NULL) { + goto ON_RELEASE; + } + + if (RedfishConfigServiceInfo->RedfishServiceUseHttps) { + AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a", AsciiLocatio= n); + } 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 !=3D NULL) { + Status =3D 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 =3D=3D NULL) { + // + // No credential available on this system. + // + DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n", __func__= )); + } else { + Status =3D Private->CredentialProtocol->GetAuthInfo ( + Private->CredentialProtocol, + &AuthMethod, + &Username, + &Password + ); + if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) || IS_EMPTY_STRIN= G (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 =3D AsciiStrSize (Username); + PasswordSize =3D AsciiStrSize (Password); + BasicAuthStrSize =3D UsernameSize + PasswordSize; // one byte take= n from null-terminator for ':' + BasicAuthString =3D AllocateZeroPool (BasicAuthStrSize); + if (BasicAuthString =3D=3D NULL) { + goto ON_RELEASE; + } + + AsciiSPrint ( + BasicAuthString, + BasicAuthStrSize, + "%a:%a", + Username, + Password + ); + + Status =3D Base64Encode ( + (CONST UINT8 *)BasicAuthString, + BasicAuthStrSize, + EncodedAuthString, + &EncodedAuthStrSize + ); + if ((Status =3D=3D EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)= ) { + EncodedAuthString =3D AllocateZeroPool (EncodedAuthStrSize); + if (EncodedAuthString =3D=3D NULL) { + goto ON_RELEASE; + } + + Status =3D 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__, S= tatus)); + goto ON_RELEASE; + } + } + } + + NewService =3D CreateRedfishService (Host, AsciiLocation, EncodedAuthStr= ing, NULL, RestEx); + if (NewService =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__)); + } + +ON_RELEASE: + + if (BasicAuthString !=3D NULL) { + ZeroMem (BasicAuthString, BasicAuthStrSize); + FreePool (BasicAuthString); + } + + if (EncodedAuthString !=3D NULL) { + ZeroMem (BasicAuthString, EncodedAuthStrSize); + FreePool (EncodedAuthString); + } + + if (Username !=3D NULL) { + ZeroMem (Username, UsernameSize); + FreePool (Username); + } + + if (Password !=3D NULL) { + ZeroMem (Password, PasswordSize); + FreePool (Password); + } + + if (AsciiLocation !=3D NULL) { + FreePool (AsciiLocation); + } + + if (Host !=3D NULL) { + FreePool (Host); + } + + return NewService; +} + +/** + This function free resources in Redfish service. RedfishService is no lo= nger available + after this function returns successfully. + + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL insta= nce. + @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 =3D=3D NULL) || (RedfishService =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + Service =3D (REDFISH_SERVICE_PRIVATE *)RedfishService; + if (Service->Signature !=3D 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 insta= nce. + @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 =3D=3D NULL) || (RedfishPayload =3D=3D NULL)) { + return NULL; + } + + Payload =3D (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload; + if (Payload->Signature !=3D 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 instanc= e. + @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 =3D=3D NULL) || (Service =3D=3D NULL) || (Response =3D=3D 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 =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + CacheData =3D NULL; + RetryCount =3D 0; + ZeroMem (Response, sizeof (REDFISH_RESPONSE)); + + if (Private->CacheDisabled) { + UseCache =3D FALSE; + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by PCD!\n", _= _func__)); + } + + // + // Search for cache list. + // + if (UseCache) { + CacheData =3D FindHttpCacheData (&Private->CacheList.Head, Uri); + if (CacheData !=3D NULL) { + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit! %s\n", __f= unc__, Uri)); + + // + // Copy cached response to caller's buffer. + // + Status =3D CopyRedfishResponse (CacheData->Response, R= esponse); + CacheData->HitCount +=3D 1; + return Status; + } + } + + // + // Get resource from redfish service. + // + do { + RetryCount +=3D 1; + Status =3D 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 >=3D Private->RetrySetting.Maxi= mumRetryGet)) { + break; + } + + // + // Retry when BMC is not ready. + // + if ((Response->StatusCode !=3D 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 =3D 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, &P= rivate->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 =3D=3D NULL) || (Request =3D=3D 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 =3D=3D NULL) || (Response =3D=3D 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 =3D=3D NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__, Uri)= ); + + Private =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + + CacheData =3D FindHttpCacheData (&Private->CacheList.Head, Uri); + if (CacheData =3D=3D 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 instanc= e. + @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 serv= ice. + This is optional. When ContentSize is 0, Conte= ntSize + is the size of Content. + @param[in] ContentType Type of the Content to be send to Redfish serv= ice. + This is optional. When ContentType is NULL, co= ntent + 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 =3D=3D NULL) || (Service =3D=3D NULL) || (Response =3D=3D 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 =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + RetryCount =3D 0; + ZeroMem (Response, sizeof (REDFISH_RESPONSE)); + ZeroMem (&Request, sizeof (REDFISH_REQUEST)); + + Request.Content =3D Content; + Request.ContentLength =3D ContentSize; + Request.ContentType =3D ContentType; + + // + // Patch resource to redfish service. + // + do { + RetryCount +=3D 1; + Status =3D 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 >=3D Private->RetrySetting.Maxi= mumRetryPatch)) { + break; + } + + // + // Retry when BMC is not ready. + // + if ((Response->StatusCode !=3D 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__, Ur= i, 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 instanc= e. + @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 serv= ice. + This is optional. When ContentSize is 0, Conte= ntSize + is the size of Content. + @param[in] ContentType Type of the Content to be send to Redfish serv= ice. + This is optional. When ContentType is NULL, co= ntent + 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 =3D=3D NULL) || (Service =3D=3D NULL) || (Response =3D=3D 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 =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + RetryCount =3D 0; + ZeroMem (Response, sizeof (REDFISH_RESPONSE)); + ZeroMem (&Request, sizeof (REDFISH_REQUEST)); + + Request.Content =3D Content; + Request.ContentLength =3D ContentSize; + Request.ContentType =3D ContentType; + + // + // Patch resource to redfish service. + // + do { + RetryCount +=3D 1; + Status =3D 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 >=3D Private->RetrySetting.Maxi= mumRetryPut)) { + break; + } + + // + // Retry when BMC is not ready. + // + if ((Response->StatusCode !=3D 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", __f= unc__, 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 instanc= e. + @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 serv= ice. + This is optional. When ContentSize is 0, Conte= ntSize + is the size of Content. + @param[in] ContentType Type of the Content to be send to Redfish serv= ice. + This is optional. When ContentType is NULL, co= ntent + 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 =3D=3D NULL) || (Service =3D=3D NULL) || (Response =3D=3D 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 =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + RetryCount =3D 0; + ZeroMem (Response, sizeof (REDFISH_RESPONSE)); + ZeroMem (&Request, sizeof (REDFISH_REQUEST)); + + Request.Content =3D Content; + Request.ContentLength =3D ContentSize; + Request.ContentType =3D ContentType; + + // + // Patch resource to redfish service. + // + do { + RetryCount +=3D 1; + Status =3D 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 >=3D Private->RetrySetting.Maxi= mumRetryPost)) { + break; + } + + // + // Retry when BMC is not ready. + // + if ((Response->StatusCode !=3D 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 instanc= e. + @param[in] Service Redfish service instance to perform HTTP DELET= E. + @param[in] Uri Target resource URI. + @param[in] Content JSON represented properties to be deleted. Thi= s is + optional. + @param[in] ContentSize Size of the Content to be send to Redfish serv= ice. + This is optional. When ContentSize is 0, Conte= ntSize + is the size of Content if Content is not NULL. + @param[in] ContentType Type of the Content to be send to Redfish serv= ice. + 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 =3D=3D NULL) || (Service =3D=3D NULL) || (Response =3D=3D NULL= ) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__, Uri)= ); + + Private =3D REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); + RetryCount =3D 0; + ZeroMem (Response, sizeof (REDFISH_RESPONSE)); + ZeroMem (&Request, sizeof (REDFISH_REQUEST)); + + Request.Content =3D Content; + Request.ContentLength =3D ContentSize; + Request.ContentType =3D ContentType; + + // + // Patch resource to redfish service. + // + do { + RetryCount +=3D 1; + Status =3D 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 >=3D Private->RetrySetting.Maxi= mumRetryDelete)) { + break; + } + + // + // Retry when BMC is not ready. + // + if ((Response->StatusCode !=3D 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__, U= ri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status)); + goto ON_RELEASE; + } + +ON_RELEASE: + + return Status; +} + +EDKII_REDFISH_HTTP_PROTOCOL mEdkIIRedfishHttpProtocol =3D { + 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 u= nloaded. + + @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 =3D=3D NULL) { + return EFI_SUCCESS; + } + + if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) { + ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList); + } + + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEdkIIRedfishHttpProtocolGuid, + &mRedfishHttpCachePrivate->Protocol, + NULL + ); + + FreePool (mRedfishHttpCachePrivate); + mRedfishHttpCachePrivate =3D 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 =3D (REDFISH_HTTP_CACHE_PRIVATE *)Context; + if (Private->Signature !=3D REDFISH_HTTP_DRIVER_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); + return; + } + + // + // Locate HII database protocol. + // + Status =3D 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 !=3D NULL) { + return EFI_ALREADY_STARTED; + } + + mRedfishHttpCachePrivate =3D AllocateZeroPool (sizeof (REDFISH_HTTP_CACH= E_PRIVATE)); + if (mRedfishHttpCachePrivate =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initial cache list and protocol instance. + // + mRedfishHttpCachePrivate->Signature =3D REDFISH_HTTP_DRIVER_SIGNATURE; + mRedfishHttpCachePrivate->ImageHandle =3D ImageHandle; + CopyMem (&mRedfishHttpCachePrivate->Protocol, &mEdkIIRedfishHttpProtocol= , sizeof (EDKII_REDFISH_HTTP_PROTOCOL)); + mRedfishHttpCachePrivate->CacheList.Capacity =3D REDFISH_HTTP_CACHE_LIST= _SIZE; + mRedfishHttpCachePrivate->CacheList.Count =3D 0x00; + mRedfishHttpCachePrivate->CacheDisabled =3D PcdGetBool (PcdHttpCach= eDisabled); + InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head); + + // + // Get retry settings + // + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet =3D PcdGet16 (= PcdHttpGetRetry); + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut =3D PcdGet16 (= PcdHttpPutRetry); + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch =3D PcdGet16 (= PcdHttpPatchRetry); + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost =3D PcdGet16 (= PcdHttpPostRetry); + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete =3D PcdGet16 (= PcdHttpDeleteRetry); + mRedfishHttpCachePrivate->RetrySetting.RetryWait =3D PcdGet16 (= PcdHttpRetryWaitInSecond) * 1000000U; + + // + // Install the gEdkIIRedfishHttpProtocolGuid onto Handle. + // + Status =3D 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 =3D EfiCreateProtocolNotifyEvent ( + &gEdkIIRedfishCredentialProtoc= olGuid, + TPL_CALLBACK, + CredentialProtocolInstalled, + mRedfishHttpCachePrivate, + &Registration + ); + if (mRedfishHttpCachePrivate->NotifyEvent =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for g= EdkIIRedfishCredentialProtocolGuid\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 =3D=3D NULL) || (SrcHeaderCount =3D=3D 0) || (DstHeaders= =3D=3D NULL) || (DstHeaderCount =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + *DstHeaderCount =3D 0; + *DstHeaders =3D AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcHead= erCount); + if (*DstHeaders =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index =3D 0; Index < SrcHeaderCount; Index++) { + (*DstHeaders)[Index].FieldName =3D AllocateCopyPool (AsciiStrSize (Src= Headers[Index].FieldName), SrcHeaders[Index].FieldName); + if ((*DstHeaders)[Index].FieldName =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + (*DstHeaders)[Index].FieldValue =3D AllocateCopyPool (AsciiStrSize (Sr= cHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue); + if ((*DstHeaders)[Index].FieldValue =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *DstHeaderCount +=3D 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((Request->Headers !=3D NULL) && (Request->HeaderCount > 0)) { + HttpFreeHeaderFields (Request->Headers, Request->HeaderCount); + Request->Headers =3D NULL; + Request->HeaderCount =3D 0; + } + + if (Request->Content !=3D NULL) { + FreePool (Request->Content); + Request->Content =3D NULL; + } + + if (Request->ContentType !=3D NULL) { + FreePool (Request->ContentType); + Request->ContentType =3D NULL; + } + + Request->ContentLength =3D 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((Response->Headers !=3D NULL) && (Response->HeaderCount > 0)) { + HttpFreeHeaderFields (Response->Headers, Response->HeaderCount); + Response->Headers =3D NULL; + Response->HeaderCount =3D 0; + } + + if (Response->Payload !=3D NULL) { + ReleaseRedfishPayload (Response->Payload); + Response->Payload =3D NULL; + } + + if (Response->StatusCode !=3D NULL) { + FreePool (Response->StatusCode); + Response->StatusCode =3D 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 messa= ge. + + @retval EFI_SUCCESS Resrouce is released successfully. + @retval Others Errors occur. + +**/ +EFI_STATUS +ReleaseHttpMessage ( + IN EFI_HTTP_MESSAGE *HttpMessage, + IN BOOLEAN IsRequest + ) +{ + if (HttpMessage =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsRequest) { + if (HttpMessage->Data.Request !=3D NULL) { + if (HttpMessage->Data.Request->Url !=3D NULL) { + FreePool (HttpMessage->Data.Request->Url); + } + + FreePool (HttpMessage->Data.Request); + HttpMessage->Data.Request =3D NULL; + } + } else { + if (HttpMessage->Data.Response !=3D NULL) { + FreePool (HttpMessage->Data.Response); + HttpMessage->Data.Response =3D NULL; + } + } + + if (HttpMessage->Body !=3D NULL) { + FreePool (HttpMessage->Body); + HttpMessage->Body =3D NULL; + } + + if (HttpMessage->Headers !=3D NULL) { + HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage->HeaderCount); + HttpMessage->Headers =3D NULL; + HttpMessage->HeaderCount =3D 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 servic= e. + 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 =3D NULL; + RequestData =3D NULL; + Url =3D NULL; + UrlSize =3D 0; + Content =3D NULL; + ContentLength =3D 0; + HeaderCount =3D REDFISH_COMMON_HEADER_SIZE; + HeaderIndex =3D 0; + Headers =3D NULL; + HasContent =3D FALSE; + DoContentEncoding =3D FALSE; + + if ((ServicePrivate =3D=3D NULL) || (IS_EMPTY_STRING (Uri))) { + return NULL; + } + + if (Method >=3D HttpMethodMax) { + return NULL; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__, Uri)); + + // + // Build full URL for HTTP query. + // + UrlSize =3D (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * si= zeof (CHAR16); + Url =3D AllocateZeroPool (UrlSize); + if (Url =3D=3D 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 !=3D NULL) && (Request->HeaderCount > 0)) { + HeaderCount +=3D 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) = !=3D 0) { + DoContentEncoding =3D TRUE; + } + } + + if ((Request !=3D NULL) && !IS_EMPTY_STRING (Request->Content)) { + HeaderCount +=3D 2; + HasContent =3D TRUE; + if (DoContentEncoding) { + HeaderCount +=3D 1; + } + } + + Headers =3D AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER)); + if (Headers =3D=3D NULL) { + goto ON_ERROR; + } + + if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) { + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEA= DER_X_AUTH_TOKEN, ServicePrivate->SessionToken); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) { + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEA= DER_AUTHORIZATION, ServicePrivate->BasicAuth); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + if (Request !=3D NULL) { + for (Index =3D 0; Index < Request->HeaderCount; Index++) { + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], Reques= t->Headers[Index].FieldName, Request->Headers[Index].FieldValue); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } + + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADE= R_HOST, ServicePrivate->HostName); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HT= TP_HEADER_ODATA_VERSION_STR, REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADE= R_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADE= R_USER_AGENT, REDFISH_HTTP_HEADER_USER_AGENT_VALUE); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HT= TP_HEADER_CONNECTION_STR, REDFISH_HTTP_HEADER_CONNECTION_VALUE); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Handle content header + // + if (HasContent) { + if (Request->ContentType =3D=3D NULL) { + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_H= EADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else { + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_H= EADER_CONTENT_TYPE, Request->ContentType); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + if (Request->ContentLength =3D=3D 0) { + Request->ContentLength =3D AsciiStrLen (Request->Content); + } + + AsciiSPrint ( + ContentLengthStr, + sizeof (ContentLengthStr), + "%lu", + (UINT64)Request->ContentLength + ); + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEA= DER_CONTENT_LENGTH, ContentLengthStr); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Encoding + // + if (DoContentEncoding) { + // + // We currently only support gzip Content-Encoding. + // + Status =3D RedfishContentEncode ( + ContentEncoding, + Request->Content, + Request->ContentLength, + &Content, + &ContentLength + ); + if (Status =3D=3D EFI_INVALID_PARAMETER) { + DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__)); + goto ON_ERROR; + } else if (Status =3D=3D EFI_UNSUPPORTED) { + DoContentEncoding =3D FALSE; + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content coding f= or %a! Use raw data instead.\n", __func__, ContentEncoding)); + Status =3D HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP= _HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_IDENTITY); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else { + Status =3D 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 pro= perly release it later. + // + if (!DoContentEncoding) { + Content =3D AllocateCopyPool (Request->ContentLength, Request->Conte= nt); + if (Content =3D=3D NULL) { + goto ON_ERROR; + } + + ContentLength =3D Request->ContentLength; + } + } + + // + // Step 2: build the rest of HTTP request info. + // + RequestData =3D AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); + if (RequestData =3D=3D NULL) { + goto ON_ERROR; + } + + RequestData->Method =3D Method; + RequestData->Url =3D Url; + + // + // Step 3: fill in EFI_HTTP_MESSAGE + // + RequestMsg =3D AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); + if (RequestMsg =3D=3D NULL) { + goto ON_ERROR; + } + + ASSERT (HeaderIndex =3D=3D HeaderCount); + RequestMsg->Data.Request =3D RequestData; + RequestMsg->HeaderCount =3D HeaderIndex; + RequestMsg->Headers =3D Headers; + + if (HasContent) { + RequestMsg->BodyLength =3D ContentLength; + RequestMsg->Body =3D Content; + } + + return RequestMsg; + +ON_ERROR: + + if (Headers !=3D NULL) { + HttpFreeHeaderFields (Headers, HeaderIndex); + } + + if (RequestData !=3D NULL) { + FreePool (RequestData); + } + + if (RequestMsg !=3D NULL) { + FreePool (RequestMsg); + } + + if (Url !=3D 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 =3D=3D NULL) || (ResponseMsg =3D=3D NULL) || (Redfis= hResponse =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__)); + + // + // Initialization + // + JsonData =3D NULL; + RedfishResponse->HeaderCount =3D 0; + RedfishResponse->Headers =3D NULL; + RedfishResponse->Payload =3D NULL; + RedfishResponse->StatusCode =3D NULL; + DecodedBody =3D NULL; + DecodedLength =3D 0; + + // + // Return the HTTP StatusCode. + // + if (ResponseMsg->Data.Response !=3D NULL) { + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n", __func__= , ResponseMsg->Data.Response->StatusCode)); + RedfishResponse->StatusCode =3D AllocateCopyPool (sizeof (EFI_HTTP_STA= TUS_CODE), &ResponseMsg->Data.Response->StatusCode); + if (RedfishResponse->StatusCode =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n", __func__= )); + } + } + + // + // Return the HTTP headers. + // + if (ResponseMsg->Headers !=3D NULL) { + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count: %d\n", __= func__, ResponseMsg->HeaderCount)); + Status =3D 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 !=3D 0) && (ResponseMsg->Body !=3D NULL)) { + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length: %d\n", __f= unc__, ResponseMsg->BodyLength)); + // + // Check if data is encoded. + // + ContentEncodedHeader =3D HttpFindHeader (RedfishResponse->HeaderCount,= RedfishResponse->Headers, HTTP_HEADER_CONTENT_ENCODING); + if (ContentEncodedHeader !=3D NULL) { + // + // The content is encoded. + // + Status =3D RedfishContentDecode ( + ContentEncodedHeader->FieldValue, + ResponseMsg->Body, + ResponseMsg->BodyLength, + &DecodedBody, + &DecodedLength + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response conten= t: %r decoding method: %a\n.", __func__, Status, ContentEncodedHeader->Fiel= dValue)); + goto ON_ERROR; + } + + JsonData =3D JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL); + FreePool (DecodedBody); + } else { + JsonData =3D JsonLoadBuffer (ResponseMsg->Body, ResponseMsg->BodyLen= gth, 0, NULL); + } + + if (!JsonValueIsNull (JsonData)) { + RedfishResponse->Payload =3D CreateRedfishPayload (ServicePrivate, J= sonData); + if (RedfishResponse->Payload =3D=3D 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 !=3D 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 =3D=3D NULL) || IS_EMPTY_STRING (Uri) || (Response =3D=3D N= ULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x %s\n", __fun= c__, Method, Uri)); + + ServicePrivate =3D (REDFISH_SERVICE_PRIVATE *)Service; + if (ServicePrivate->Signature !=3D REDFISH_HTTP_SERVICE_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); + HttpContentEncoding =3D (CHAR8 *)PcdGetPtr (PcdRedfishServiceContentEnco= ding); + + RequestMsg =3D BuildRequestMessage (Service, Uri, Method, Request, HttpC= ontentEncoding); + if (RequestMsg =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n", __fu= nc__, Uri)); + return EFI_PROTOCOL_ERROR; + } + + // + // call RESTEx to get response from REST service. + // + RestExStatus =3D ServicePrivate->RestEx->SendReceive (ServicePrivate->Re= stEx, 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 =3D 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 =3D=3D HttpMethodPost) && + (Response->StatusCode !=3D NULL) && + ((*Response->StatusCode =3D=3D HTTP_STATUS_200_OK) || (*Response->= StatusCode =3D=3D HTTP_STATUS_204_NO_CONTENT))) + { + XAuthTokenHeader =3D HttpFindHeader (ResponseMsg.HeaderCount, Respon= seMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN); + if (XAuthTokenHeader !=3D NULL) { + Status =3D UpdateSessionToken (ServicePrivate, XAuthTokenHeader->F= ieldValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n", _= _func__, Status)); + } + } + } + } + + // + // Release resources + // + if (RequestMsg !=3D 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
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved= . +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. # # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -20,4 +20,5 @@ INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf INF MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf + INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf !endif -- 2.34.1 -The information contained in this message may be confidential and propriet= ary to American Megatrends (AMI). This communication is intended to be read= only by the individual or entity to whom it is addressed or by their desig= nee. If the reader of this message is not the intended recipient, you are o= n notice that any distribution of this message, in any form, is strictly pr= ohibited. Please promptly notify the sender by reply e-mail or by telephone= at 770-246-8600, and then delete or destroy all copies of the transmission= . -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#115820): https://edk2.groups.io/g/devel/message/115820 Mute This Topic: https://groups.io/mt/104505404/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-