mirror of
				https://gitlab.com/qemu-project/edk2.git
				synced 2025-10-30 07:56:39 +08:00 
			
		
		
		
	RedfishPkg: implement Redfish HTTP protocol
implement Redfish HTTP protocol driver. Signed-off-by: Nickle Wang <nicklew@nvidia.com> Co-authored-by: Igor Kulchytskyy <igork@ami.com> Cc: Abner Chang <abner.chang@amd.com> Cc: Igor Kulchytskyy <igork@ami.com> Cc: Nick Ramirez <nramirez@nvidia.com> Reviewed-by: Abner Chang <abner.chang@amd.com> Reviewed-by: Igor Kulchytskyy <igork@ami.com> Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
This commit is contained in:
		 Nickle Wang
					Nickle Wang
				
			
				
					committed by
					
						![mergify[bot]](/assets/img/avatar_default.png) mergify[bot]
						mergify[bot]
					
				
			
			
				
	
			
			
			![mergify[bot]](/assets/img/avatar_default.png) mergify[bot]
						mergify[bot]
					
				
			
						parent
						
							1988f2df29
						
					
				
				
					commit
					0ce2012c6c
				
			| @ -6,7 +6,7 @@ | ||||
| # to be built in the firmware volume. | ||||
| # | ||||
| # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR> | ||||
| # Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
| # Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
| # | ||||
| #    SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
| # | ||||
| @ -20,4 +20,5 @@ | ||||
|   INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf | ||||
|   INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf | ||||
|   INF MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf | ||||
|   INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf | ||||
| !endif | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| # "RedfishDefines.dsc.inc". | ||||
| # | ||||
| # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR> | ||||
| # Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
| # Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
| # | ||||
| #    SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
| # | ||||
| @ -28,4 +28,5 @@ | ||||
|   RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf | ||||
|   RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf | ||||
|   MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf | ||||
|   RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf | ||||
| !endif | ||||
|  | ||||
							
								
								
									
										667
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										667
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,667 @@ | ||||
| /** @file | ||||
|   RedfishHttpData handles internal data to support Redfish HTTP protocol. | ||||
|  | ||||
|   Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
|  | ||||
|   SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
|  | ||||
| **/ | ||||
|  | ||||
| #include "RedfishHttpData.h" | ||||
| #include "RedfishHttpOperation.h" | ||||
|  | ||||
| /** | ||||
|   This function update session token in Redfish Service. | ||||
|  | ||||
|   @param[in]  Service         Pointer to service instance. | ||||
|   @param[in]  Token           Session token. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Session token is updated. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| UpdateSessionToken ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service, | ||||
|   IN CHAR8                    *Token | ||||
|   ) | ||||
| { | ||||
|   if ((Service == NULL) || IS_EMPTY_STRING (Token)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (Service->SessionToken != NULL) { | ||||
|     FreePool (Service->SessionToken); | ||||
|   } | ||||
|  | ||||
|   Service->SessionToken = ASCII_STR_DUPLICATE (Token); | ||||
|   if (Service->SessionToken == NULL) { | ||||
|     return EFI_OUT_OF_RESOURCES; | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function release Redfish Service. | ||||
|  | ||||
|   @param[in]  Service         Pointer to service instance. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Service is released. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishService ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service | ||||
|   ) | ||||
| { | ||||
|   if (Service == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (Service->Host != NULL) { | ||||
|     FreePool (Service->Host); | ||||
|   } | ||||
|  | ||||
|   if (Service->HostName != NULL) { | ||||
|     FreePool (Service->HostName); | ||||
|   } | ||||
|  | ||||
|   if (Service->BasicAuth != NULL) { | ||||
|     ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth)); | ||||
|     FreePool (Service->BasicAuth); | ||||
|   } | ||||
|  | ||||
|   if (Service->SessionToken != NULL) { | ||||
|     ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken)); | ||||
|     FreePool (Service->SessionToken); | ||||
|   } | ||||
|  | ||||
|   FreePool (Service); | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function creat new service. Host and HostName are copied to | ||||
|   newly created service instance. | ||||
|  | ||||
|   @param[in]  Host            Host string. | ||||
|   @param[in]  HostName        Hostname string. | ||||
|   @param[in]  BasicAuth       Basic Authorization string. | ||||
|   @param[in]  SessionToken    Session token string. | ||||
|   @param[in]  RestEx          Rest EX protocol instance. | ||||
|  | ||||
|   @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service. | ||||
|   @retval     NULL             Error occurs. | ||||
|  | ||||
| **/ | ||||
| REDFISH_SERVICE_PRIVATE * | ||||
| CreateRedfishService ( | ||||
|   IN CHAR8                 *Host, | ||||
|   IN CHAR8                 *HostName, | ||||
|   IN CHAR8                 *BasicAuth OPTIONAL, | ||||
|   IN CHAR8                 *SessionToken OPTIONAL, | ||||
|   IN EFI_REST_EX_PROTOCOL  *RestEx | ||||
|   ) | ||||
| { | ||||
|   REDFISH_SERVICE_PRIVATE  *NewService; | ||||
|   UINTN                    AuthStrSize; | ||||
|  | ||||
|   if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx == NULL)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE)); | ||||
|   if (NewService == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE; | ||||
|   NewService->Host      = ASCII_STR_DUPLICATE (Host); | ||||
|   if (NewService->Host == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   NewService->HostName = ASCII_STR_DUPLICATE (HostName); | ||||
|   if (NewService->HostName == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   if (!IS_EMPTY_STRING (BasicAuth)) { | ||||
|     AuthStrSize           = AsciiStrSize (BasicAuth) + AsciiStrLen (REDFISH_HTTP_BASIC_AUTH_STR); | ||||
|     NewService->BasicAuth = AllocateZeroPool (AuthStrSize); | ||||
|     if (NewService->BasicAuth == NULL) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|  | ||||
|     AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a", REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth); | ||||
|   } | ||||
|  | ||||
|   if (!IS_EMPTY_STRING (SessionToken)) { | ||||
|     NewService->SessionToken = ASCII_STR_DUPLICATE (SessionToken); | ||||
|     if (NewService->SessionToken == NULL) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   NewService->RestEx = RestEx; | ||||
|  | ||||
|   return NewService; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   ReleaseRedfishService (NewService); | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function release Redfish Payload. | ||||
|  | ||||
|   @param[in]  Payload         Pointer to payload instance. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Payload is released. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishPayload ( | ||||
|   IN REDFISH_PAYLOAD_PRIVATE  *Payload | ||||
|   ) | ||||
| { | ||||
|   if (Payload == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (Payload->Service != NULL) { | ||||
|     ReleaseRedfishService (Payload->Service); | ||||
|   } | ||||
|  | ||||
|   if (Payload->JsonValue != NULL) { | ||||
|     JsonValueFree (Payload->JsonValue); | ||||
|   } | ||||
|  | ||||
|   FreePool (Payload); | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function creat new payload. Server and JsonObj are | ||||
|   copied to newly created payload. | ||||
|  | ||||
|   @param[in]  Service          Pointer to Service instance. | ||||
|   @param[in]  JsonValue        Pointer to JSON value. | ||||
|  | ||||
|   @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload. | ||||
|   @retval     NULL                     Error occurs. | ||||
|  | ||||
| **/ | ||||
| REDFISH_PAYLOAD_PRIVATE * | ||||
| CreateRedfishPayload ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service, | ||||
|   IN EDKII_JSON_VALUE         JsonValue | ||||
|   ) | ||||
| { | ||||
|   REDFISH_PAYLOAD_PRIVATE  *NewPayload; | ||||
|  | ||||
|   if ((Service == NULL) || (JsonValue == NULL)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE)); | ||||
|   if (NewPayload == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE; | ||||
|   NewPayload->Service   = CreateRedfishService (Service->Host, Service->HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx); | ||||
|   if (NewPayload->Service == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   NewPayload->JsonValue = JsonValueClone (JsonValue); | ||||
|   if (NewPayload->JsonValue == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   return NewPayload; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   ReleaseRedfishPayload (NewPayload); | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function copy the data in SrcResponse to DstResponse. | ||||
|  | ||||
|   @param[in]  SrcResponse      Source Response to copy. | ||||
|   @param[out] DstResponse      Destination Response. | ||||
|  | ||||
|   @retval     EFI_SUCCESS      Response is copied successfully. | ||||
|   @retval     Others           Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| CopyRedfishResponse ( | ||||
|   IN  REDFISH_RESPONSE  *SrcResponse, | ||||
|   OUT REDFISH_RESPONSE  *DstResponse | ||||
|   ) | ||||
| { | ||||
|   REDFISH_PAYLOAD_PRIVATE  *Payload; | ||||
|   UINTN                    Index; | ||||
|  | ||||
|   if ((SrcResponse == NULL) || (DstResponse == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (SrcResponse == DstResponse) { | ||||
|     return EFI_SUCCESS; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Status code | ||||
|   // | ||||
|   if (SrcResponse->StatusCode != NULL) { | ||||
|     DstResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode); | ||||
|     if (DstResponse->StatusCode == NULL) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Header | ||||
|   // | ||||
|   if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) { | ||||
|     DstResponse->HeaderCount = 0; | ||||
|     DstResponse->Headers     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcResponse->HeaderCount); | ||||
|     if (DstResponse->Headers == NULL) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|  | ||||
|     DstResponse->HeaderCount = SrcResponse->HeaderCount; | ||||
|  | ||||
|     for (Index = 0; Index < SrcResponse->HeaderCount; Index++) { | ||||
|       DstResponse->Headers[Index].FieldName = ASCII_STR_DUPLICATE (SrcResponse->Headers[Index].FieldName); | ||||
|       if (DstResponse->Headers[Index].FieldName == NULL) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|  | ||||
|       DstResponse->Headers[Index].FieldValue = ASCII_STR_DUPLICATE (SrcResponse->Headers[Index].FieldValue); | ||||
|       if (DstResponse->Headers[Index].FieldValue == NULL) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Payload | ||||
|   // | ||||
|   if (SrcResponse->Payload != NULL) { | ||||
|     Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload; | ||||
|     if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) { | ||||
|       DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|  | ||||
|     DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload->JsonValue); | ||||
|     if (DstResponse->Payload  == NULL) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   ReleaseRedfishResponse (DstResponse); | ||||
|  | ||||
|   return EFI_OUT_OF_RESOURCES; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function clone input response and return to caller | ||||
|  | ||||
|   @param[in]  Response      Response to clone. | ||||
|  | ||||
|   @retval     REDFISH_RESPONSE *  Response is cloned. | ||||
|   @retval     NULL                Errors occur. | ||||
|  | ||||
| **/ | ||||
| REDFISH_RESPONSE * | ||||
| CloneRedfishResponse ( | ||||
|   IN REDFISH_RESPONSE  *Response | ||||
|   ) | ||||
| { | ||||
|   EFI_STATUS        Status; | ||||
|   REDFISH_RESPONSE  *NewResponse; | ||||
|  | ||||
|   if (Response == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE)); | ||||
|   if (NewResponse == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   Status = CopyRedfishResponse (Response, NewResponse); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     FreePool (NewResponse); | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   return NewResponse; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Release REDFISH_HTTP_CACHE_DATA resource | ||||
|  | ||||
|   @param[in]    Data    Pointer to REDFISH_HTTP_CACHE_DATA instance | ||||
|  | ||||
|   @retval EFI_SUCCESS             REDFISH_HTTP_CACHE_DATA is released successfully. | ||||
|   @retval EFI_INVALID_PARAMETER   Data is NULL | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseHttpCacheData ( | ||||
|   IN REDFISH_HTTP_CACHE_DATA  *Data | ||||
|   ) | ||||
| { | ||||
|   if (Data == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (Data->Uri != NULL) { | ||||
|     FreePool (Data->Uri); | ||||
|   } | ||||
|  | ||||
|   if (Data->Response != NULL) { | ||||
|     ReleaseRedfishResponse (Data->Response); | ||||
|     FreePool (Data->Response); | ||||
|   } | ||||
|  | ||||
|   FreePool (Data); | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Create new cache data. | ||||
|  | ||||
|   @param[in]    Uri       The URI string matching to this cache data. | ||||
|   @param[in]    Response  HTTP response. | ||||
|  | ||||
|   @retval REDFISH_HTTP_CACHE_DATA *   Pointer to newly created cache data. | ||||
|   @retval NULL                        No memory available. | ||||
|  | ||||
| **/ | ||||
| REDFISH_HTTP_CACHE_DATA * | ||||
| NewHttpCacheData ( | ||||
|   IN  EFI_STRING        Uri, | ||||
|   IN  REDFISH_RESPONSE  *Response | ||||
|   ) | ||||
| { | ||||
|   REDFISH_HTTP_CACHE_DATA  *NewData; | ||||
|   UINTN                    Size; | ||||
|  | ||||
|   if (IS_EMPTY_STRING (Uri) || (Response == NULL)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA)); | ||||
|   if (NewData == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE; | ||||
|   Size               = StrSize (Uri); | ||||
|   NewData->Uri       = AllocateCopyPool (Size, Uri); | ||||
|   if (NewData->Uri == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   NewData->Response = Response; | ||||
|   NewData->HitCount = 1; | ||||
|  | ||||
|   return NewData; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   if (NewData != NULL) { | ||||
|     ReleaseHttpCacheData (NewData); | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Search on given ListHeader for given URI string. | ||||
|  | ||||
|   @param[in]    ListHeader  Target list to search. | ||||
|   @param[in]    Uri         Target URI to search. | ||||
|  | ||||
|   @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found. | ||||
|   @retval NULL                      No cache data with given URI is found. | ||||
|  | ||||
| **/ | ||||
| REDFISH_HTTP_CACHE_DATA * | ||||
| FindHttpCacheData ( | ||||
|   IN  LIST_ENTRY  *ListHeader, | ||||
|   IN  EFI_STRING  Uri | ||||
|   ) | ||||
| { | ||||
|   LIST_ENTRY               *List; | ||||
|   REDFISH_HTTP_CACHE_DATA  *Data; | ||||
|  | ||||
|   if (IS_EMPTY_STRING (Uri)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   if (IsListEmpty (ListHeader)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   Data = NULL; | ||||
|   List = GetFirstNode (ListHeader); | ||||
|   while (!IsNull (ListHeader, List)) { | ||||
|     Data = REDFISH_HTTP_CACHE_FROM_LIST (List); | ||||
|  | ||||
|     if (StrCmp (Data->Uri, Uri) == 0) { | ||||
|       return Data; | ||||
|     } | ||||
|  | ||||
|     List = GetNextNode (ListHeader, List); | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Search on given ListHeader and return cache data with minimum hit count. | ||||
|  | ||||
|   @param[in]    ListHeader  Target list to search. | ||||
|  | ||||
|   @retval REDFISH_HTTP_CACHE_DATA   Target cache data is returned. | ||||
|   @retval NULL                      No cache data is found. | ||||
|  | ||||
| **/ | ||||
| REDFISH_HTTP_CACHE_DATA * | ||||
| FindUnusedHttpCacheData ( | ||||
|   IN  LIST_ENTRY  *ListHeader | ||||
|   ) | ||||
| { | ||||
|   LIST_ENTRY               *List; | ||||
|   REDFISH_HTTP_CACHE_DATA  *Data; | ||||
|   REDFISH_HTTP_CACHE_DATA  *UnusedData; | ||||
|   UINTN                    HitCount; | ||||
|  | ||||
|   if (IsListEmpty (ListHeader)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   Data       = NULL; | ||||
|   UnusedData = NULL; | ||||
|   HitCount   = 0; | ||||
|  | ||||
|   List       = GetFirstNode (ListHeader); | ||||
|   Data       = REDFISH_HTTP_CACHE_FROM_LIST (List); | ||||
|   UnusedData = Data; | ||||
|   HitCount   = Data->HitCount; | ||||
|   List       = GetNextNode (ListHeader, List); | ||||
|  | ||||
|   while (!IsNull (ListHeader, List)) { | ||||
|     Data = REDFISH_HTTP_CACHE_FROM_LIST (List); | ||||
|  | ||||
|     if (Data->HitCount < HitCount) { | ||||
|       HitCount   = Data->HitCount; | ||||
|       UnusedData = Data; | ||||
|     } | ||||
|  | ||||
|     List = GetNextNode (ListHeader, List); | ||||
|   } | ||||
|  | ||||
|   return UnusedData; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Delete a cache data by given cache instance. | ||||
|  | ||||
|   @param[in]    List    Target cache list to be removed. | ||||
|   @param[in]    Data    Pointer to the instance to be deleted. | ||||
|  | ||||
|   @retval EFI_SUCCESS   Cache data is removed. | ||||
|   @retval Others        Fail to remove cache data. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| DeleteHttpCacheData ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *List, | ||||
|   IN  REDFISH_HTTP_CACHE_DATA  *Data | ||||
|   ) | ||||
| { | ||||
|   if ((List == NULL) || (Data == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__, Data->Uri)); | ||||
|  | ||||
|   RemoveEntryList (&Data->List); | ||||
|   --List->Count; | ||||
|  | ||||
|   return ReleaseHttpCacheData (Data); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Add new cache by given URI and HTTP response to specify List. | ||||
|  | ||||
|   @param[in]    List      Target cache list to add. | ||||
|   @param[in]    Uri       The URI string matching to this cache data. | ||||
|   @param[in]    Response  HTTP response. | ||||
|  | ||||
|   @retval EFI_SUCCESS   Cache data is added. | ||||
|   @retval Others        Fail to add cache data. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| AddHttpCacheData ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *List, | ||||
|   IN  EFI_STRING               Uri, | ||||
|   IN  REDFISH_RESPONSE         *Response | ||||
|   ) | ||||
| { | ||||
|   REDFISH_HTTP_CACHE_DATA  *NewData; | ||||
|   REDFISH_HTTP_CACHE_DATA  *OldData; | ||||
|   REDFISH_HTTP_CACHE_DATA  *UnusedData; | ||||
|   REDFISH_RESPONSE         *NewResponse; | ||||
|  | ||||
|   if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // If same cache data exist, replace it with latest one. | ||||
|   // | ||||
|   OldData = FindHttpCacheData (&List->Head, Uri); | ||||
|   if (OldData != NULL) { | ||||
|     DeleteHttpCacheData (List, OldData); | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Check capacity | ||||
|   // | ||||
|   if (List->Count >= List->Capacity) { | ||||
|     DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused cache\n", __func__)); | ||||
|     UnusedData = FindUnusedHttpCacheData (&List->Head); | ||||
|     if (UnusedData == NULL) { | ||||
|       return EFI_OUT_OF_RESOURCES; | ||||
|     } | ||||
|  | ||||
|     DeleteHttpCacheData (List, UnusedData); | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Clone a local copy | ||||
|   // | ||||
|   NewResponse = CloneRedfishResponse (Response); | ||||
|   if (NewResponse == NULL) { | ||||
|     return EFI_OUT_OF_RESOURCES; | ||||
|   } | ||||
|  | ||||
|   NewData = NewHttpCacheData (Uri, NewResponse); | ||||
|   if (NewData == NULL) { | ||||
|     return EFI_OUT_OF_RESOURCES; | ||||
|   } | ||||
|  | ||||
|   InsertTailList (&List->Head, &NewData->List); | ||||
|   ++List->Count; | ||||
|  | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n", __func__, List->Count, List->Capacity, NewData->Uri)); | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Release all cache from list. | ||||
|  | ||||
|   @param[in]    CacheList    The list to be released. | ||||
|  | ||||
|   @retval EFI_SUCCESS             All cache data are released. | ||||
|   @retval EFI_INVALID_PARAMETER   CacheList is NULL. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseCacheList ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *CacheList | ||||
|   ) | ||||
| { | ||||
|   LIST_ENTRY               *List; | ||||
|   LIST_ENTRY               *Next; | ||||
|   REDFISH_HTTP_CACHE_DATA  *Data; | ||||
|  | ||||
|   if (CacheList == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (IsListEmpty (&CacheList->Head)) { | ||||
|     return EFI_SUCCESS; | ||||
|   } | ||||
|  | ||||
|   Data = NULL; | ||||
|   Next = NULL; | ||||
|   List = GetFirstNode (&CacheList->Head); | ||||
|   while (!IsNull (&CacheList->Head, List)) { | ||||
|     Data = REDFISH_HTTP_CACHE_FROM_LIST (List); | ||||
|     Next = GetNextNode (&CacheList->Head, List); | ||||
|  | ||||
|     DeleteHttpCacheData (CacheList, Data); | ||||
|  | ||||
|     List = Next; | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
							
								
								
									
										256
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | ||||
| /** @file | ||||
|   Definitions of RedfishHttpData | ||||
|  | ||||
|   Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
|  | ||||
|   SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
|  | ||||
| **/ | ||||
|  | ||||
| #ifndef EDKII_REDFISH_HTTP_DATA_H_ | ||||
| #define EDKII_REDFISH_HTTP_DATA_H_ | ||||
|  | ||||
| #include "RedfishHttpDxe.h" | ||||
|  | ||||
| #define REDFISH_HTTP_DRIVER_SIGNATURE   SIGNATURE_32 ('r', 'f', 'h', 'p') | ||||
| #define REDFISH_HTTP_CACHE_SIGNATURE    SIGNATURE_32 ('r', 'f', 'c', 'h') | ||||
| #define REDFISH_HTTP_SERVICE_SIGNATURE  SIGNATURE_32 ('r', 'f', 's', 'v') | ||||
| #define REDFISH_HTTP_PAYLOAD_SIGNATURE  SIGNATURE_32 ('r', 'f', 'p', 'l') | ||||
| #define REDFISH_HTTP_BASIC_AUTH_STR     "Basic " | ||||
|  | ||||
| /// | ||||
| /// REDFISH_SERVICE_PRIVATE definition. | ||||
| /// | ||||
| typedef struct { | ||||
|   UINT32                  Signature; | ||||
|   CHAR8                   *Host; | ||||
|   CHAR8                   *HostName; | ||||
|   CHAR8                   *BasicAuth; | ||||
|   CHAR8                   *SessionToken; | ||||
|   EFI_REST_EX_PROTOCOL    *RestEx; | ||||
| } REDFISH_SERVICE_PRIVATE; | ||||
|  | ||||
| /// | ||||
| /// REDFISH_PAYLOAD_PRIVATE definition. | ||||
| /// | ||||
| typedef struct { | ||||
|   UINT32                     Signature; | ||||
|   REDFISH_SERVICE_PRIVATE    *Service; | ||||
|   EDKII_JSON_VALUE           JsonValue; | ||||
| } REDFISH_PAYLOAD_PRIVATE; | ||||
|  | ||||
| /// | ||||
| /// Definition of REDFISH_HTTP_CACHE_DATA | ||||
| /// | ||||
| typedef struct { | ||||
|   UINT32              Signature; | ||||
|   LIST_ENTRY          List; | ||||
|   EFI_STRING          Uri; | ||||
|   UINTN               HitCount; | ||||
|   REDFISH_RESPONSE    *Response; | ||||
| } REDFISH_HTTP_CACHE_DATA; | ||||
|  | ||||
| #define REDFISH_HTTP_CACHE_FROM_LIST(a)  CR (a, REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE) | ||||
|  | ||||
| /// | ||||
| /// Definition of REDFISH_HTTP_CACHE_LIST | ||||
| /// | ||||
| typedef struct { | ||||
|   LIST_ENTRY    Head; | ||||
|   UINTN         Count; | ||||
|   UINTN         Capacity; | ||||
| } REDFISH_HTTP_CACHE_LIST; | ||||
|  | ||||
| /// | ||||
| /// Definition of REDFISH_HTTP_RETRY_SETTING | ||||
| /// | ||||
| typedef struct { | ||||
|   UINT16    MaximumRetryGet; | ||||
|   UINT16    MaximumRetryPut; | ||||
|   UINT16    MaximumRetryPost; | ||||
|   UINT16    MaximumRetryPatch; | ||||
|   UINT16    MaximumRetryDelete; | ||||
|   UINTN     RetryWait; | ||||
| } REDFISH_HTTP_RETRY_SETTING; | ||||
|  | ||||
| /// | ||||
| /// Definition of REDFISH_HTTP_CACHE_PRIVATE | ||||
| /// | ||||
| typedef struct { | ||||
|   UINT32                               Signature; | ||||
|   EFI_HANDLE                           ImageHandle; | ||||
|   BOOLEAN                              CacheDisabled; | ||||
|   EFI_EVENT                            NotifyEvent; | ||||
|   REDFISH_HTTP_CACHE_LIST              CacheList; | ||||
|   EDKII_REDFISH_HTTP_PROTOCOL          Protocol; | ||||
|   EDKII_REDFISH_CREDENTIAL_PROTOCOL    *CredentialProtocol; | ||||
|   REDFISH_HTTP_RETRY_SETTING           RetrySetting; | ||||
| } REDFISH_HTTP_CACHE_PRIVATE; | ||||
|  | ||||
| #define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a)  CR (a, REDFISH_HTTP_CACHE_PRIVATE, Protocol, REDFISH_HTTP_DRIVER_SIGNATURE) | ||||
|  | ||||
| /** | ||||
|   Search on given ListHeader for given URI string. | ||||
|  | ||||
|   @param[in]    ListHeader  Target list to search. | ||||
|   @param[in]    Uri         Target URI to search. | ||||
|  | ||||
|   @retval REDFISH_HTTP_CACHE_DATA   Target cache data is found. | ||||
|   @retval NULL                      No cache data with given URI is found. | ||||
|  | ||||
| **/ | ||||
| REDFISH_HTTP_CACHE_DATA * | ||||
| FindHttpCacheData ( | ||||
|   IN  LIST_ENTRY  *ListHeader, | ||||
|   IN  EFI_STRING  Uri | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function copy the data in SrcResponse to DstResponse. | ||||
|  | ||||
|   @param[in]  SrcResponse      Source Response to copy. | ||||
|   @param[out] DstResponse      Destination Response. | ||||
|  | ||||
|   @retval     EFI_SUCCESS      Response is copied successfully. | ||||
|   @retval     Others           Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| CopyRedfishResponse ( | ||||
|   IN  REDFISH_RESPONSE  *SrcResponse, | ||||
|   OUT REDFISH_RESPONSE  *DstResponse | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   Release all cache from list. | ||||
|  | ||||
|   @param[in]    CacheList    The list to be released. | ||||
|  | ||||
|   @retval EFI_SUCCESS             All cache data are released. | ||||
|   @retval EFI_INVALID_PARAMETER   CacheList is NULL. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseCacheList ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *CacheList | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   Add new cache by given URI and HTTP response to specify List. | ||||
|  | ||||
|   @param[in]    List      Target cache list to add. | ||||
|   @param[in]    Uri       The URI string matching to this cache data. | ||||
|   @param[in]    Response  HTTP response. | ||||
|  | ||||
|   @retval EFI_SUCCESS   Cache data is added. | ||||
|   @retval Others        Fail to add cache data. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| AddHttpCacheData ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *List, | ||||
|   IN  EFI_STRING               Uri, | ||||
|   IN  REDFISH_RESPONSE         *Response | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   Delete a cache data by given cache instance. | ||||
|  | ||||
|   @param[in]    List    Target cache list to be removed. | ||||
|   @param[in]    Data    Pointer to the instance to be deleted. | ||||
|  | ||||
|   @retval EFI_SUCCESS   Cache data is removed. | ||||
|   @retval Others        Fail to remove cache data. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| DeleteHttpCacheData ( | ||||
|   IN  REDFISH_HTTP_CACHE_LIST  *List, | ||||
|   IN  REDFISH_HTTP_CACHE_DATA  *Data | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function release Redfish Payload. | ||||
|  | ||||
|   @param[in]  Payload         Pointer to payload instance. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Payload is released. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishPayload ( | ||||
|   IN REDFISH_PAYLOAD_PRIVATE  *Payload | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function creat new payload. Server and JsonObj are | ||||
|   copied to newly created payload. | ||||
|  | ||||
|   @param[in]  Service          Pointer to Service instance. | ||||
|   @param[in]  JsonObj          Pointer to JSON object. | ||||
|  | ||||
|   @retval     REDFISH_PAYLOAD_PRIVATE  Newly created payload. | ||||
|   @retval     NULL             Error occurs. | ||||
|  | ||||
| **/ | ||||
| REDFISH_PAYLOAD_PRIVATE * | ||||
| CreateRedfishPayload ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service, | ||||
|   IN EDKII_JSON_VALUE         JsonValue | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function release Redfish Service. | ||||
|  | ||||
|   @param[in]  Service         Pointer to service instance. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Service is released. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishService ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function creat new service. Host and HostName are copied to | ||||
|   newly created service instance. | ||||
|  | ||||
|   @param[in]  Host            Host string. | ||||
|   @param[in]  HostName        Hostname string. | ||||
|   @param[in]  BasicAuth       Basic Authorization string. | ||||
|   @param[in]  SessionToken    Session token string. | ||||
|   @param[in]  RestEx          Rest EX protocol instance. | ||||
|  | ||||
|   @retval     REDFISH_PAYLOAD_PRIVATE  Newly created service. | ||||
|   @retval     NULL             Error occurs. | ||||
|  | ||||
| **/ | ||||
| REDFISH_SERVICE_PRIVATE * | ||||
| CreateRedfishService ( | ||||
|   IN CHAR8                 *Host, | ||||
|   IN CHAR8                 *HostName, | ||||
|   IN CHAR8                 *BasicAuth OPTIONAL, | ||||
|   IN CHAR8                 *SessionToken OPTIONAL, | ||||
|   IN EFI_REST_EX_PROTOCOL  *RestEx | ||||
|   ); | ||||
|  | ||||
| /** | ||||
|   This function update session token in Redfish Service. | ||||
|  | ||||
|   @param[in]  Service         Pointer to service instance. | ||||
|   @param[in]  Token           Session token. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Session token is updated. | ||||
|   @retval     Others          Error occurs. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| UpdateSessionToken ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *Service, | ||||
|   IN CHAR8                    *Token | ||||
|   ); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										1344
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1344
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										44
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| /** @file | ||||
|   Definitions of RedfishHttpDxe | ||||
|  | ||||
|   Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
|  | ||||
|   SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
|  | ||||
| **/ | ||||
|  | ||||
| #ifndef EDKII_REDFISH_HTTP_DXE_H_ | ||||
| #define EDKII_REDFISH_HTTP_DXE_H_ | ||||
|  | ||||
| #include <Uefi.h> | ||||
| #include <IndustryStandard/Http11.h> | ||||
|  | ||||
| #include <Library/UefiLib.h> | ||||
| #include <Library/BaseLib.h> | ||||
| #include <Library/BaseMemoryLib.h> | ||||
| #include <Library/RedfishContentCodingLib.h> | ||||
| #include <Library/DebugLib.h> | ||||
| #include <Library/HttpLib.h> | ||||
| #include <Library/JsonLib.h> | ||||
| #include <Library/UefiBootServicesTableLib.h> | ||||
| #include <Library/MemoryAllocationLib.h> | ||||
| #include <Library/RedfishDebugLib.h> | ||||
| #include <Library/ReportStatusCodeLib.h> | ||||
| #include <Library/PrintLib.h> | ||||
|  | ||||
| #include <Protocol/Http.h> | ||||
| #include <Protocol/EdkIIRedfishHttpProtocol.h> | ||||
| #include <Protocol/EdkIIRedfishCredential.h> | ||||
| #include <Protocol/RestEx.h> | ||||
|  | ||||
| #define IS_EMPTY_STRING(a)  ((a) == NULL || (a)[0] == '\0') | ||||
| #define REDFISH_HTTP_CACHE_LIST_SIZE      0x80 | ||||
| #define REDFISH_ERROR_MSG_MAX             128 | ||||
| #define REDFISH_DEBUG_STRING_LENGTH       200 | ||||
| #define REDFISH_HOST_NAME_MAX             64   // IPv6 maximum length (39) + "https://" (8) + port number (maximum 5) | ||||
| #define REDFISH_HTTP_ERROR_REPORT         "Redfish HTTP %a failure(0x%x): %s" | ||||
| #define REDFISH_HTTP_CACHE_DEBUG          DEBUG_MANAGEABILITY | ||||
| #define REDFISH_HTTP_CACHE_DEBUG_DUMP     DEBUG_MANAGEABILITY | ||||
| #define REDFISH_HTTP_CACHE_DEBUG_REQUEST  DEBUG_MANAGEABILITY | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										73
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| ## @file | ||||
| #  RedfishHttpDxe is the DXE driver which provides | ||||
| #  EdkIIRedfishHttpProtocol to EDK2 Redfish Feature | ||||
| #  drivers for HTTP operation. | ||||
| # | ||||
| #  Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
| # | ||||
| #  SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
| # | ||||
| ## | ||||
|  | ||||
| [Defines] | ||||
|   INF_VERSION                    = 0x0001000b | ||||
|   BASE_NAME                      = RedfishHttpDxe | ||||
|   FILE_GUID                      = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0 | ||||
|   MODULE_TYPE                    = DXE_DRIVER | ||||
|   VERSION_STRING                 = 1.0 | ||||
|   ENTRY_POINT                    = RedfishHttpEntryPoint | ||||
|   UNLOAD_IMAGE                   = RedfishHttpDriverUnload | ||||
|  | ||||
| # | ||||
| #  VALID_ARCHITECTURES           = IA32 X64 ARM AARCH64 RISCV64 | ||||
| # | ||||
|  | ||||
| [Sources] | ||||
|   RedfishHttpData.c | ||||
|   RedfishHttpData.h | ||||
|   RedfishHttpDxe.c | ||||
|   RedfishHttpDxe.h | ||||
|   RedfishHttpOperation.c | ||||
|   RedfishHttpOperation.h | ||||
|  | ||||
| [Packages] | ||||
|   MdePkg/MdePkg.dec | ||||
|   MdeModulePkg/MdeModulePkg.dec | ||||
|   NetworkPkg/NetworkPkg.dec | ||||
|   RedfishPkg/RedfishPkg.dec | ||||
|  | ||||
| [LibraryClasses.ARM] | ||||
|   ArmSoftFloatLib | ||||
|  | ||||
| [LibraryClasses] | ||||
|   BaseLib | ||||
|   BaseMemoryLib | ||||
|   RedfishContentCodingLib | ||||
|   DebugLib | ||||
|   HttpLib | ||||
|   JsonLib | ||||
|   MemoryAllocationLib | ||||
|   PrintLib | ||||
|   RedfishDebugLib | ||||
|   ReportStatusCodeLib | ||||
|   UefiBootServicesTableLib | ||||
|   UefiDriverEntryPoint | ||||
|   UefiLib | ||||
|  | ||||
| [Protocols] | ||||
|   gEdkIIRedfishHttpProtocolGuid             ## PRODUCED | ||||
|   gEdkIIRedfishCredentialProtocolGuid       ## CONSUMES | ||||
|   gEfiRestExProtocolGuid                    ## CONSUEMS | ||||
|  | ||||
| [Pcd] | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding | ||||
|  | ||||
| [Depex] | ||||
|   TRUE | ||||
							
								
								
									
										692
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										692
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,692 @@ | ||||
| /** @file | ||||
|   RedfishHttpOperation handles HTTP operations. | ||||
|  | ||||
|   Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||||
|  | ||||
|   SPDX-License-Identifier: BSD-2-Clause-Patent | ||||
|  | ||||
| **/ | ||||
|  | ||||
| #include "RedfishHttpOperation.h" | ||||
| #include "RedfishHttpData.h" | ||||
|  | ||||
| /** | ||||
|   This function copies all headers in SrcHeaders to DstHeaders. | ||||
|   It's call responsibility to release returned DstHeaders. | ||||
|  | ||||
|   @param[in]  SrcHeaders      Source headers. | ||||
|   @param[in]  SrcHeaderCount  Number of header in source headers. | ||||
|   @param[out] DstHeaders      Destination headers. | ||||
|   @param[out] DstHeaderCount  Number of header in designation headers. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Headers are copied successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| CopyHttpHeaders ( | ||||
|   IN  EFI_HTTP_HEADER  *SrcHeaders, | ||||
|   IN  UINTN            SrcHeaderCount, | ||||
|   OUT EFI_HTTP_HEADER  **DstHeaders, | ||||
|   OUT UINTN            *DstHeaderCount | ||||
|   ) | ||||
| { | ||||
|   UINTN  Index; | ||||
|  | ||||
|   if ((SrcHeaders == NULL) || (SrcHeaderCount == 0) || (DstHeaders == NULL) || (DstHeaderCount == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   *DstHeaderCount = 0; | ||||
|   *DstHeaders     = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcHeaderCount); | ||||
|   if (*DstHeaders == NULL) { | ||||
|     return EFI_OUT_OF_RESOURCES; | ||||
|   } | ||||
|  | ||||
|   *DstHeaderCount = SrcHeaderCount; | ||||
|   for (Index = 0; Index < SrcHeaderCount; Index++) { | ||||
|     (*DstHeaders)[Index].FieldName = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldName); | ||||
|     if ((*DstHeaders)[Index].FieldName == NULL) { | ||||
|       return EFI_OUT_OF_RESOURCES; | ||||
|     } | ||||
|  | ||||
|     (*DstHeaders)[Index].FieldValue = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldValue); | ||||
|     if ((*DstHeaders)[Index].FieldValue == NULL) { | ||||
|       return EFI_OUT_OF_RESOURCES; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   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     Resource is released successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishRequest ( | ||||
|   IN  REDFISH_REQUEST  *Request | ||||
|   ) | ||||
| { | ||||
|   if (Request == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if ((Request->Headers != NULL) && (Request->HeaderCount > 0)) { | ||||
|     HttpFreeHeaderFields (Request->Headers, Request->HeaderCount); | ||||
|     Request->Headers     = NULL; | ||||
|     Request->HeaderCount = 0; | ||||
|   } | ||||
|  | ||||
|   if (Request->Content != NULL) { | ||||
|     FreePool (Request->Content); | ||||
|     Request->Content = NULL; | ||||
|   } | ||||
|  | ||||
|   if (Request->ContentType != NULL) { | ||||
|     FreePool (Request->ContentType); | ||||
|     Request->ContentType = NULL; | ||||
|   } | ||||
|  | ||||
|   Request->ContentLength = 0; | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function free resources in given Response. | ||||
|  | ||||
|   @param[in]  Response     HTTP response to be released. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Resource is released successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseRedfishResponse ( | ||||
|   IN  REDFISH_RESPONSE  *Response | ||||
|   ) | ||||
| { | ||||
|   if (Response == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if ((Response->Headers != NULL) && (Response->HeaderCount > 0)) { | ||||
|     HttpFreeHeaderFields (Response->Headers, Response->HeaderCount); | ||||
|     Response->Headers     = NULL; | ||||
|     Response->HeaderCount = 0; | ||||
|   } | ||||
|  | ||||
|   if (Response->Payload != NULL) { | ||||
|     ReleaseRedfishPayload (Response->Payload); | ||||
|     Response->Payload = NULL; | ||||
|   } | ||||
|  | ||||
|   if (Response->StatusCode != NULL) { | ||||
|     FreePool (Response->StatusCode); | ||||
|     Response->StatusCode = NULL; | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function free resources in given HTTP message. | ||||
|  | ||||
|   @param[in]  HttpMessage     HTTP message to be released. | ||||
|   @param[in]  IsRequest       TRUE if this is request type of HTTP message. | ||||
|                               FALSE if this is response type of HTTP message. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Resource is released successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ReleaseHttpMessage ( | ||||
|   IN  EFI_HTTP_MESSAGE  *HttpMessage, | ||||
|   IN  BOOLEAN           IsRequest | ||||
|   ) | ||||
| { | ||||
|   if (HttpMessage == NULL) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   if (IsRequest) { | ||||
|     if (HttpMessage->Data.Request != NULL) { | ||||
|       if (HttpMessage->Data.Request->Url != NULL) { | ||||
|         FreePool (HttpMessage->Data.Request->Url); | ||||
|       } | ||||
|  | ||||
|       FreePool (HttpMessage->Data.Request); | ||||
|       HttpMessage->Data.Request = NULL; | ||||
|     } | ||||
|   } else { | ||||
|     if (HttpMessage->Data.Response != NULL) { | ||||
|       FreePool (HttpMessage->Data.Response); | ||||
|       HttpMessage->Data.Response = NULL; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (HttpMessage->Body != NULL) { | ||||
|     FreePool (HttpMessage->Body); | ||||
|     HttpMessage->Body = NULL; | ||||
|   } | ||||
|  | ||||
|   if (HttpMessage->Headers != NULL) { | ||||
|     HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage->HeaderCount); | ||||
|     HttpMessage->Headers     = NULL; | ||||
|     HttpMessage->HeaderCount = 0; | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function build Redfish message for sending data to Redfish service. | ||||
|   It's call responsibility to properly release returned HTTP message by | ||||
|   calling ReleaseHttpMessage. | ||||
|  | ||||
|   @param[in]   ServicePrivate    Pointer to Redfish service private data. | ||||
|   @param[in]   Uri               Redfish service URI. | ||||
|   @param[in]   Method            HTTP method. | ||||
|   @param[in]   Request           Additional data to send to Redfish service. | ||||
|                                  This is optional. | ||||
|   @param[in]   ContentEncoding   Content encoding method to compress HTTP context. | ||||
|                                  This is optional. When ContentEncoding is NULL, | ||||
|                                  No compress method will be performed. | ||||
|  | ||||
|   @retval     EFI_HTTP_MESSAGE *   Pointer to newly created HTTP message. | ||||
|   @retval     NULL                 Error occurred. | ||||
|  | ||||
| **/ | ||||
| EFI_HTTP_MESSAGE * | ||||
| BuildRequestMessage ( | ||||
|   IN REDFISH_SERVICE_PRIVATE  *ServicePrivate, | ||||
|   IN EFI_STRING               Uri, | ||||
|   IN EFI_HTTP_METHOD          Method, | ||||
|   IN REDFISH_REQUEST          *Request OPTIONAL, | ||||
|   IN CHAR8                    *ContentEncoding OPTIONAL | ||||
|   ) | ||||
| { | ||||
|   EFI_STATUS             Status; | ||||
|   EFI_STRING             Url; | ||||
|   UINTN                  UrlSize; | ||||
|   UINTN                  Index; | ||||
|   EFI_HTTP_MESSAGE       *RequestMsg; | ||||
|   EFI_HTTP_REQUEST_DATA  *RequestData; | ||||
|   UINTN                  HeaderCount; | ||||
|   UINTN                  HeaderIndex; | ||||
|   EFI_HTTP_HEADER        *Headers; | ||||
|   CHAR8                  ContentLengthStr[REDFISH_CONTENT_LENGTH_SIZE]; | ||||
|   VOID                   *Content; | ||||
|   UINTN                  ContentLength; | ||||
|   BOOLEAN                HasContent; | ||||
|   BOOLEAN                DoContentEncoding; | ||||
|  | ||||
|   RequestMsg        = NULL; | ||||
|   RequestData       = NULL; | ||||
|   Url               = NULL; | ||||
|   UrlSize           = 0; | ||||
|   Content           = NULL; | ||||
|   ContentLength     = 0; | ||||
|   HeaderCount       = REDFISH_COMMON_HEADER_SIZE; | ||||
|   HeaderIndex       = 0; | ||||
|   Headers           = NULL; | ||||
|   HasContent        = FALSE; | ||||
|   DoContentEncoding = FALSE; | ||||
|  | ||||
|   if ((ServicePrivate == NULL) || (IS_EMPTY_STRING (Uri))) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   if (Method >= HttpMethodMax) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__, Uri)); | ||||
|  | ||||
|   // | ||||
|   // Build full URL for HTTP query. | ||||
|   // | ||||
|   UrlSize = (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * sizeof (CHAR16); | ||||
|   Url     = AllocateZeroPool (UrlSize); | ||||
|   if (Url == NULL) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   UnicodeSPrint (Url, UrlSize, L"%a%s", ServicePrivate->Host, Uri); | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Url: %s\n", __func__, Url)); | ||||
|  | ||||
|   // | ||||
|   // Step 1: build the HTTP headers. | ||||
|   // | ||||
|   if (!IS_EMPTY_STRING (ServicePrivate->SessionToken) || !IS_EMPTY_STRING (ServicePrivate->BasicAuth)) { | ||||
|     HeaderCount++; | ||||
|   } | ||||
|  | ||||
|   if ((Request != NULL) && (Request->HeaderCount > 0)) { | ||||
|     HeaderCount += Request->HeaderCount; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Check and see if we will do content encoding or not | ||||
|   // | ||||
|   if (!IS_EMPTY_STRING (ContentEncoding)) { | ||||
|     if (AsciiStrCmp (ContentEncoding, REDFISH_HTTP_CONTENT_ENCODING_NONE) != 0) { | ||||
|       DoContentEncoding = TRUE; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if ((Request != NULL) && !IS_EMPTY_STRING (Request->Content)) { | ||||
|     HeaderCount += 2; | ||||
|     HasContent   = TRUE; | ||||
|     if (DoContentEncoding) { | ||||
|       HeaderCount += 1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Headers = AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER)); | ||||
|   if (Headers == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) { | ||||
|     Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_X_AUTH_TOKEN, ServicePrivate->SessionToken); | ||||
|     if (EFI_ERROR (Status)) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|   } else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) { | ||||
|     Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_AUTHORIZATION, ServicePrivate->BasicAuth); | ||||
|     if (EFI_ERROR (Status)) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (Request != NULL) { | ||||
|     for (Index = 0; Index < Request->HeaderCount; Index++) { | ||||
|       Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], Request->Headers[Index].FieldName, Request->Headers[Index].FieldValue); | ||||
|       if (EFI_ERROR (Status)) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_HOST, ServicePrivate->HostName); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_ODATA_VERSION_STR, REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_USER_AGENT, REDFISH_HTTP_HEADER_USER_AGENT_VALUE); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_CONNECTION_STR, REDFISH_HTTP_HEADER_CONNECTION_VALUE); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Handle content header | ||||
|   // | ||||
|   if (HasContent) { | ||||
|     if (Request->ContentType == NULL) { | ||||
|       Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON); | ||||
|       if (EFI_ERROR (Status)) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|     } else { | ||||
|       Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, Request->ContentType); | ||||
|       if (EFI_ERROR (Status)) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (Request->ContentLength == 0) { | ||||
|       Request->ContentLength =  AsciiStrLen (Request->Content); | ||||
|     } | ||||
|  | ||||
|     AsciiSPrint ( | ||||
|       ContentLengthStr, | ||||
|       sizeof (ContentLengthStr), | ||||
|       "%lu", | ||||
|       (UINT64)Request->ContentLength | ||||
|       ); | ||||
|     Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_LENGTH, ContentLengthStr); | ||||
|     if (EFI_ERROR (Status)) { | ||||
|       goto ON_ERROR; | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // Encoding | ||||
|     // | ||||
|     if (DoContentEncoding) { | ||||
|       // | ||||
|       // We currently only support gzip Content-Encoding. | ||||
|       // | ||||
|       Status =  RedfishContentEncode ( | ||||
|                   ContentEncoding, | ||||
|                   Request->Content, | ||||
|                   Request->ContentLength, | ||||
|                   &Content, | ||||
|                   &ContentLength | ||||
|                   ); | ||||
|       if (Status == EFI_INVALID_PARAMETER) { | ||||
|         DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__)); | ||||
|         goto ON_ERROR; | ||||
|       } else if (Status == EFI_UNSUPPORTED) { | ||||
|         DoContentEncoding = FALSE; | ||||
|         DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content coding for %a! Use raw data instead.\n", __func__, ContentEncoding)); | ||||
|         Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_IDENTITY); | ||||
|         if (EFI_ERROR (Status)) { | ||||
|           goto ON_ERROR; | ||||
|         } | ||||
|       } else { | ||||
|         Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_GZIP); | ||||
|         if (EFI_ERROR (Status)) { | ||||
|           goto ON_ERROR; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // When the content is from caller, we use our own copy so that we properly release it later. | ||||
|     // | ||||
|     if (!DoContentEncoding) { | ||||
|       Content = AllocateCopyPool (Request->ContentLength, Request->Content); | ||||
|       if (Content == NULL) { | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|  | ||||
|       ContentLength = Request->ContentLength; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Step 2: build the rest of HTTP request info. | ||||
|   // | ||||
|   RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); | ||||
|   if (RequestData == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   RequestData->Method = Method; | ||||
|   RequestData->Url    = Url; | ||||
|  | ||||
|   // | ||||
|   // Step 3: fill in EFI_HTTP_MESSAGE | ||||
|   // | ||||
|   RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); | ||||
|   if (RequestMsg == NULL) { | ||||
|     goto ON_ERROR; | ||||
|   } | ||||
|  | ||||
|   ASSERT (HeaderIndex == HeaderCount); | ||||
|   RequestMsg->Data.Request = RequestData; | ||||
|   RequestMsg->HeaderCount  = HeaderIndex; | ||||
|   RequestMsg->Headers      = Headers; | ||||
|  | ||||
|   if (HasContent) { | ||||
|     RequestMsg->BodyLength = ContentLength; | ||||
|     RequestMsg->Body       = Content; | ||||
|   } | ||||
|  | ||||
|   return RequestMsg; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   if (Headers != NULL) { | ||||
|     HttpFreeHeaderFields (Headers, HeaderIndex); | ||||
|   } | ||||
|  | ||||
|   if (RequestData != NULL) { | ||||
|     FreePool (RequestData); | ||||
|   } | ||||
|  | ||||
|   if (RequestMsg != NULL) { | ||||
|     FreePool (RequestMsg); | ||||
|   } | ||||
|  | ||||
|   if (Url != NULL) { | ||||
|     FreePool (Url); | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function parse response message from Redfish service, and | ||||
|   build Redfish response for caller. It's call responsibility to | ||||
|   properly release Redfish response by calling ReleaseRedfishResponse. | ||||
|  | ||||
|   @param[in]   ServicePrivate   Pointer to Redfish service private data. | ||||
|   @param[in]   ResponseMsg      Response message from Redfish service. | ||||
|   @param[out]  RedfishResponse  Redfish response data. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Redfish response is returned successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| ParseResponseMessage ( | ||||
|   IN  REDFISH_SERVICE_PRIVATE  *ServicePrivate, | ||||
|   IN  EFI_HTTP_MESSAGE         *ResponseMsg, | ||||
|   OUT REDFISH_RESPONSE         *RedfishResponse | ||||
|   ) | ||||
| { | ||||
|   EFI_STATUS        Status; | ||||
|   EDKII_JSON_VALUE  JsonData; | ||||
|   EFI_HTTP_HEADER   *ContentEncodedHeader; | ||||
|   VOID              *DecodedBody; | ||||
|   UINTN             DecodedLength; | ||||
|  | ||||
|   if ((ServicePrivate == NULL) || (ResponseMsg == NULL) || (RedfishResponse == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__)); | ||||
|  | ||||
|   // | ||||
|   // Initialization | ||||
|   // | ||||
|   JsonData                     = NULL; | ||||
|   RedfishResponse->HeaderCount = 0; | ||||
|   RedfishResponse->Headers     = NULL; | ||||
|   RedfishResponse->Payload     = NULL; | ||||
|   RedfishResponse->StatusCode  = NULL; | ||||
|   DecodedBody                  = NULL; | ||||
|   DecodedLength                = 0; | ||||
|  | ||||
|   // | ||||
|   // Return the HTTP StatusCode. | ||||
|   // | ||||
|   if (ResponseMsg->Data.Response != NULL) { | ||||
|     DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n", __func__, ResponseMsg->Data.Response->StatusCode)); | ||||
|     RedfishResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), &ResponseMsg->Data.Response->StatusCode); | ||||
|     if (RedfishResponse->StatusCode == NULL) { | ||||
|       DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n", __func__)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Return the HTTP headers. | ||||
|   // | ||||
|   if (ResponseMsg->Headers != NULL) { | ||||
|     DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count: %d\n", __func__, ResponseMsg->HeaderCount)); | ||||
|     Status = CopyHttpHeaders ( | ||||
|                ResponseMsg->Headers, | ||||
|                ResponseMsg->HeaderCount, | ||||
|                &RedfishResponse->Headers, | ||||
|                &RedfishResponse->HeaderCount | ||||
|                ); | ||||
|     if (EFI_ERROR (Status)) { | ||||
|       DEBUG ((DEBUG_ERROR, "%a: Failed to copy HTTP headers: %r\n", __func__, Status)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Return the HTTP body. | ||||
|   // | ||||
|   if ((ResponseMsg->BodyLength != 0) && (ResponseMsg->Body != NULL)) { | ||||
|     DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length: %d\n", __func__, ResponseMsg->BodyLength)); | ||||
|     // | ||||
|     // Check if data is encoded. | ||||
|     // | ||||
|     ContentEncodedHeader = HttpFindHeader (RedfishResponse->HeaderCount, RedfishResponse->Headers, HTTP_HEADER_CONTENT_ENCODING); | ||||
|     if (ContentEncodedHeader != NULL) { | ||||
|       // | ||||
|       // The content is encoded. | ||||
|       // | ||||
|       Status = RedfishContentDecode ( | ||||
|                  ContentEncodedHeader->FieldValue, | ||||
|                  ResponseMsg->Body, | ||||
|                  ResponseMsg->BodyLength, | ||||
|                  &DecodedBody, | ||||
|                  &DecodedLength | ||||
|                  ); | ||||
|       if (EFI_ERROR (Status)) { | ||||
|         DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response content: %r decoding method: %a\n.", __func__, Status, ContentEncodedHeader->FieldValue)); | ||||
|         goto ON_ERROR; | ||||
|       } | ||||
|  | ||||
|       JsonData = JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL); | ||||
|       FreePool (DecodedBody); | ||||
|     } else { | ||||
|       JsonData = JsonLoadBuffer (ResponseMsg->Body, ResponseMsg->BodyLength, 0, NULL); | ||||
|     } | ||||
|  | ||||
|     if (!JsonValueIsNull (JsonData)) { | ||||
|       RedfishResponse->Payload = CreateRedfishPayload (ServicePrivate, JsonData); | ||||
|       if (RedfishResponse->Payload == NULL) { | ||||
|         DEBUG ((DEBUG_ERROR, "%a: Failed to create payload\n.", __func__)); | ||||
|       } | ||||
|  | ||||
|       JsonValueFree (JsonData); | ||||
|     } else { | ||||
|       DEBUG ((DEBUG_ERROR, "%a: No payload available\n", __func__)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return EFI_SUCCESS; | ||||
|  | ||||
| ON_ERROR: | ||||
|  | ||||
|   if (RedfishResponse != NULL) { | ||||
|     ReleaseRedfishResponse (RedfishResponse); | ||||
|   } | ||||
|  | ||||
|   return Status; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   This function send Redfish request to Redfish service by calling | ||||
|   Rest Ex protocol. | ||||
|  | ||||
|   @param[in]   Service       Pointer to Redfish service. | ||||
|   @param[in]   Uri           Uri of Redfish service. | ||||
|   @param[in]   Method        HTTP method. | ||||
|   @param[in]   Request     Request data. This is optional. | ||||
|   @param[out]  Response    Redfish response data. | ||||
|  | ||||
|   @retval     EFI_SUCCESS     Request is sent and received successfully. | ||||
|   @retval     Others          Errors occur. | ||||
|  | ||||
| **/ | ||||
| EFI_STATUS | ||||
| HttpSendReceive ( | ||||
|   IN  REDFISH_SERVICE   Service, | ||||
|   IN  EFI_STRING        Uri, | ||||
|   IN  EFI_HTTP_METHOD   Method, | ||||
|   IN  REDFISH_REQUEST   *Request  OPTIONAL, | ||||
|   OUT REDFISH_RESPONSE  *Response | ||||
|   ) | ||||
| { | ||||
|   EFI_STATUS               Status; | ||||
|   EFI_STATUS               RestExStatus; | ||||
|   EFI_HTTP_MESSAGE         *RequestMsg; | ||||
|   EFI_HTTP_MESSAGE         ResponseMsg; | ||||
|   REDFISH_SERVICE_PRIVATE  *ServicePrivate; | ||||
|   EFI_HTTP_HEADER          *XAuthTokenHeader; | ||||
|   CHAR8                    *HttpContentEncoding; | ||||
|  | ||||
|   if ((Service == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) { | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x %s\n", __func__, Method, Uri)); | ||||
|  | ||||
|   ServicePrivate = (REDFISH_SERVICE_PRIVATE *)Service; | ||||
|   if (ServicePrivate->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) { | ||||
|     DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | ||||
|     return EFI_INVALID_PARAMETER; | ||||
|   } | ||||
|  | ||||
|   ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); | ||||
|   HttpContentEncoding = (CHAR8 *)PcdGetPtr (PcdRedfishServiceContentEncoding); | ||||
|  | ||||
|   RequestMsg = BuildRequestMessage (Service, Uri, Method, Request, HttpContentEncoding); | ||||
|   if (RequestMsg == NULL) { | ||||
|     DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n", __func__, Uri)); | ||||
|     return EFI_PROTOCOL_ERROR; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // call RESTEx to get response from REST service. | ||||
|   // | ||||
|   RestExStatus = ServicePrivate->RestEx->SendReceive (ServicePrivate->RestEx, RequestMsg, &ResponseMsg); | ||||
|   if (EFI_ERROR (RestExStatus)) { | ||||
|     DEBUG ((DEBUG_ERROR, "%a: %s SendReceive failure: %r\n", __func__, Uri, RestExStatus)); | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Return status code, headers and payload to caller as much as possible even when RestEx returns failure. | ||||
|   // | ||||
|   Status = ParseResponseMessage (ServicePrivate, &ResponseMsg, Response); | ||||
|   if (EFI_ERROR (Status)) { | ||||
|     DEBUG ((DEBUG_ERROR, "%a: %s parse response failure: %r\n", __func__, Uri, Status)); | ||||
|   } else { | ||||
|     // | ||||
|     // Capture session token in header | ||||
|     // | ||||
|     if ((Method == HttpMethodPost) && | ||||
|         (Response->StatusCode != NULL) && | ||||
|         ((*Response->StatusCode == HTTP_STATUS_200_OK) || (*Response->StatusCode == HTTP_STATUS_204_NO_CONTENT))) | ||||
|     { | ||||
|       XAuthTokenHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN); | ||||
|       if (XAuthTokenHeader != NULL) { | ||||
|         Status = UpdateSessionToken (ServicePrivate, XAuthTokenHeader->FieldValue); | ||||
|         if (EFI_ERROR (Status)) { | ||||
|           DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n", __func__, Status)); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Release resources | ||||
|   // | ||||
|   if (RequestMsg != NULL) { | ||||
|     ReleaseHttpMessage (RequestMsg, TRUE); | ||||
|     FreePool (RequestMsg); | ||||
|   } | ||||
|  | ||||
|   ReleaseHttpMessage (&ResponseMsg, FALSE); | ||||
|  | ||||
|   return RestExStatus; | ||||
| } | ||||
							
								
								
									
										77
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| /** @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" | ||||
| #define ASCII_STR_DUPLICATE(a)  (AllocateCopyPool (AsciiStrSize ((a)), (a))) | ||||
|  | ||||
| /** | ||||
|   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     Resource 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     Resource 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 | ||||
| @ -157,8 +157,11 @@ | ||||
|   # set to EFI_REST_EX_PROTOCOL. | ||||
|   # | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT32|0x00001009 | ||||
|   ## This is used to enable HTTP content encoding on Redfish communication. | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLEAN|0x0000100A | ||||
|   # | ||||
|   # This PCD string is introduced for platform developer to set the encoding method supported by BMC Redfish. | ||||
|   # Currently only "None" and "gzip" are supported. | ||||
|   # | ||||
|   gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|VOID*|0x0000100A | ||||
|   # | ||||
|   # Use below PCDs to control Redfhs HTTP protocol. | ||||
|   # | ||||
|  | ||||
| @ -45,6 +45,8 @@ | ||||
|   UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf | ||||
|   RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/PlatformCredentialLibNull.inf | ||||
|   RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/RedfishContentCodingLibNull.inf | ||||
|   ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf | ||||
|   SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf | ||||
|  | ||||
|   # NULL instance of IPMI related library. | ||||
|   IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf | ||||
|  | ||||
		Reference in New Issue
	
	Block a user