--- /dev/null
+/** @file\r
+ HttpIoLib.h.\r
+\r
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#ifndef HTTP_IO_LIB_H_\r
+#define HTTP_IO_LIB_H_\r
+\r
+#include <IndustryStandard/Http11.h>\r
+\r
+#include <Library/DpcLib.h>\r
+#include <Library/HttpLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#define HTTP_IO_MAX_SEND_PAYLOAD 1024\r
+#define HTTP_IO_CHUNK_SIZE_STRING_LEN 50\r
+#define HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH 256\r
+\r
+///\r
+/// HTTP_IO_CALLBACK_EVENT\r
+///\r
+typedef enum {\r
+ HttpIoRequest,\r
+ HttpIoResponse\r
+} HTTP_IO_CALLBACK_EVENT;\r
+\r
+/**\r
+ HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.\r
+\r
+ @param[in] EventType Indicate the Event type that occurs in the current callback.\r
+ @param[in] Message HTTP message which will be send to, or just received from HTTP server.\r
+ @param[in] Context The Callback Context pointer.\r
+\r
+ @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.\r
+ @retval Others Tells the HttpIo to abort the current HTTP process.\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI * HTTP_IO_CALLBACK) (\r
+ IN HTTP_IO_CALLBACK_EVENT EventType,\r
+ IN EFI_HTTP_MESSAGE *Message,\r
+ IN VOID *Context\r
+ );\r
+\r
+///\r
+/// A wrapper structure to hold the received HTTP response data.\r
+///\r
+typedef struct {\r
+ EFI_HTTP_RESPONSE_DATA Response;\r
+ UINTN HeaderCount;\r
+ EFI_HTTP_HEADER *Headers;\r
+ UINTN BodyLength;\r
+ CHAR8 *Body;\r
+ EFI_STATUS Status;\r
+} HTTP_IO_RESPONSE_DATA;\r
+\r
+///\r
+/// HTTP_IO configuration data for IPv4\r
+///\r
+typedef struct {\r
+ EFI_HTTP_VERSION HttpVersion;\r
+ UINT32 RequestTimeOut; ///< In milliseconds.\r
+ UINT32 ResponseTimeOut; ///< In milliseconds.\r
+ BOOLEAN UseDefaultAddress;\r
+ EFI_IPv4_ADDRESS LocalIp;\r
+ EFI_IPv4_ADDRESS SubnetMask;\r
+ UINT16 LocalPort;\r
+} HTTP4_IO_CONFIG_DATA;\r
+\r
+///\r
+/// HTTP_IO configuration data for IPv6\r
+///\r
+typedef struct {\r
+ EFI_HTTP_VERSION HttpVersion;\r
+ UINT32 RequestTimeOut; ///< In milliseconds.\r
+ BOOLEAN UseDefaultAddress;\r
+ EFI_IPv6_ADDRESS LocalIp;\r
+ UINT16 LocalPort;\r
+} HTTP6_IO_CONFIG_DATA;\r
+\r
+///\r
+/// HTTP_IO configuration\r
+///\r
+typedef union {\r
+ HTTP4_IO_CONFIG_DATA Config4;\r
+ HTTP6_IO_CONFIG_DATA Config6;\r
+} HTTP_IO_CONFIG_DATA;\r
+\r
+///\r
+/// HTTP_IO wrapper of the EFI HTTP service.\r
+///\r
+typedef struct {\r
+ UINT8 IpVersion;\r
+ EFI_HANDLE Image;\r
+ EFI_HANDLE Controller;\r
+ EFI_HANDLE Handle;\r
+\r
+ EFI_HTTP_PROTOCOL *Http;\r
+\r
+ HTTP_IO_CALLBACK Callback;\r
+ VOID *Context;\r
+\r
+ EFI_HTTP_TOKEN ReqToken;\r
+ EFI_HTTP_MESSAGE ReqMessage;\r
+ EFI_HTTP_TOKEN RspToken;\r
+ EFI_HTTP_MESSAGE RspMessage;\r
+\r
+ BOOLEAN IsTxDone;\r
+ BOOLEAN IsRxDone;\r
+\r
+ EFI_EVENT TimeoutEvent;\r
+ UINT32 Timeout;\r
+} HTTP_IO;\r
+\r
+///\r
+/// Process code of HTTP chunk transfer.\r
+///\r
+typedef enum {\r
+ HttpIoSendChunkNone = 0,\r
+ HttpIoSendChunkHeaderZeroContent,\r
+ HttpIoSendChunkContent,\r
+ HttpIoSendChunkEndChunk,\r
+ HttpIoSendChunkFinish\r
+} HTTP_IO_SEND_CHUNK_PROCESS;\r
+\r
+///\r
+/// Process code of HTTP non chunk transfer.\r
+///\r
+typedef enum {\r
+ HttpIoSendNonChunkNone = 0,\r
+ HttpIoSendNonChunkHeaderZeroContent,\r
+ HttpIoSendNonChunkContent,\r
+ HttpIoSendNonChunkFinish\r
+} HTTP_IO_SEND_NON_CHUNK_PROCESS;\r
+\r
+///\r
+/// Chunk links for HTTP chunked transfer coding.\r
+///\r
+typedef struct {\r
+ LIST_ENTRY NextChunk;\r
+ UINTN Length;\r
+ CHAR8 *Data;\r
+} HTTP_IO_CHUNKS;\r
+\r
+/**\r
+ Notify the callback function when an event is triggered.\r
+\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HttpIoNotifyDpc (\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event The event signaled.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HttpIoNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Destroy the HTTP_IO and release the resources.\r
+\r
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.\r
+\r
+**/\r
+VOID\r
+HttpIoDestroyIo (\r
+ IN HTTP_IO *HttpIo\r
+ );\r
+\r
+/**\r
+ Create a HTTP_IO to access the HTTP service. It will create and configure\r
+ a HTTP child handle.\r
+\r
+ @param[in] Image The handle of the driver image.\r
+ @param[in] Controller The handle of the controller.\r
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.\r
+ @param[in] ConfigData The HTTP_IO configuration data.\r
+ @param[in] Callback Callback function which will be invoked when specified\r
+ HTTP_IO_CALLBACK_EVENT happened.\r
+ @param[in] Context The Context data which will be passed to the Callback function.\r
+ @param[out] HttpIo The HTTP_IO.\r
+\r
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not\r
+ supported in the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval Others Failed to create the HTTP_IO or configure it.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoCreateIo (\r
+ IN EFI_HANDLE Image,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINT8 IpVersion,\r
+ IN HTTP_IO_CONFIG_DATA *ConfigData,\r
+ IN HTTP_IO_CALLBACK Callback,\r
+ IN VOID *Context,\r
+ OUT HTTP_IO *HttpIo\r
+ );\r
+\r
+/**\r
+ Synchronously send a HTTP REQUEST message to the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] Request A pointer to storage such data as URL and HTTP method.\r
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[in] BodyLength Length in bytes of the HTTP body.\r
+ @param[in] Body Body associated with the HTTP request.\r
+\r
+ @retval EFI_SUCCESS The HTTP request is transmitted.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoSendRequest (\r
+ IN HTTP_IO *HttpIo,\r
+ IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers, OPTIONAL\r
+ IN UINTN BodyLength,\r
+ IN VOID *Body OPTIONAL\r
+ );\r
+\r
+/**\r
+ Synchronously receive a HTTP RESPONSE message from the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).\r
+ FALSE to continue receive the previous response message.\r
+ @param[out] ResponseData Point to a wrapper of the received response data.\r
+\r
+ @retval EFI_SUCCESS The HTTP response is received.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoRecvResponse (\r
+ IN HTTP_IO *HttpIo,\r
+ IN BOOLEAN RecvMsgHeader,\r
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData\r
+ );\r
+\r
+/**\r
+ Get the value of the content length if there is a "Content-Length" header.\r
+\r
+ @param[in] HeaderCount Number of HTTP header structures in Headers.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[out] ContentLength Pointer to save the value of the content length.\r
+\r
+ @retval EFI_SUCCESS Successfully get the content length.\r
+ @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoGetContentLength (\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers,\r
+ OUT UINTN *ContentLength\r
+ );\r
+\r
+/**\r
+ Synchronously receive a HTTP RESPONSE message from the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] HeaderCount Number of headers in Headers.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[out] ChunkListHead A pointer to receivce list head of chunked data.\r
+ Caller has to release memory of ChunkListHead\r
+ and all list entries.\r
+ @param[out] ContentLength Total content length\r
+\r
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.\r
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_INVALID_PARAMETER Improper parameters.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoGetChunkedTransferContent (\r
+ IN HTTP_IO *HttpIo,\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers,\r
+ OUT LIST_ENTRY **ChunkListHead,\r
+ OUT UINTN *ContentLength\r
+ );\r
+\r
+/**\r
+ Send HTTP request in chunks.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] SendChunkProcess Pointer to current chunk process status.\r
+ @param[out] RequestMessage Request to send.\r
+\r
+ @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.\r
+ @retval Other Other errors.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoSendChunkedTransfer (\r
+ IN HTTP_IO *HttpIo,\r
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,\r
+ IN EFI_HTTP_MESSAGE *RequestMessage\r
+);\r
+#endif\r
--- /dev/null
+/** @file\r
+ Http IO Helper Library.\r
+\r
+ (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Http.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/HttpIoLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+/**\r
+ Notify the callback function when an event is triggered.\r
+\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HttpIoNotifyDpc (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event The event signaled.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HttpIoNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK\r
+ //\r
+ QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);\r
+}\r
+\r
+/**\r
+ Destroy the HTTP_IO and release the resources.\r
+\r
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.\r
+\r
+**/\r
+VOID\r
+HttpIoDestroyIo (\r
+ IN HTTP_IO *HttpIo\r
+ )\r
+{\r
+ EFI_HTTP_PROTOCOL *Http;\r
+ EFI_EVENT Event;\r
+\r
+ if (HttpIo == NULL) {\r
+ return;\r
+ }\r
+\r
+ Event = HttpIo->ReqToken.Event;\r
+ if (Event != NULL) {\r
+ gBS->CloseEvent (Event);\r
+ }\r
+\r
+ Event = HttpIo->RspToken.Event;\r
+ if (Event != NULL) {\r
+ gBS->CloseEvent (Event);\r
+ }\r
+\r
+ Event = HttpIo->TimeoutEvent;\r
+ if (Event != NULL) {\r
+ gBS->CloseEvent (Event);\r
+ }\r
+\r
+ Http = HttpIo->Http;\r
+ if (Http != NULL) {\r
+ Http->Configure (Http, NULL);\r
+ gBS->CloseProtocol (\r
+ HttpIo->Handle,\r
+ &gEfiHttpProtocolGuid,\r
+ HttpIo->Image,\r
+ HttpIo->Controller\r
+ );\r
+ }\r
+\r
+ NetLibDestroyServiceChild (\r
+ HttpIo->Controller,\r
+ HttpIo->Image,\r
+ &gEfiHttpServiceBindingProtocolGuid,\r
+ HttpIo->Handle\r
+ );\r
+}\r
+\r
+/**\r
+ Create a HTTP_IO to access the HTTP service. It will create and configure\r
+ a HTTP child handle.\r
+\r
+ @param[in] Image The handle of the driver image.\r
+ @param[in] Controller The handle of the controller.\r
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.\r
+ @param[in] ConfigData The HTTP_IO configuration data ,\r
+ NULL means not to configure the HTTP child.\r
+ @param[in] Callback Callback function which will be invoked when specified\r
+ HTTP_IO_CALLBACK_EVENT happened.\r
+ @param[in] Context The Context data which will be passed to the Callback function.\r
+ @param[out] HttpIo The HTTP_IO.\r
+\r
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not\r
+ supported in the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval Others Failed to create the HTTP_IO or configure it.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoCreateIo (\r
+ IN EFI_HANDLE Image,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINT8 IpVersion,\r
+ IN HTTP_IO_CONFIG_DATA *ConfigData, OPTIONAL\r
+ IN HTTP_IO_CALLBACK Callback,\r
+ IN VOID *Context,\r
+ OUT HTTP_IO *HttpIo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_CONFIG_DATA HttpConfigData;\r
+ EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;\r
+ EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;\r
+ EFI_HTTP_PROTOCOL *Http;\r
+ EFI_EVENT Event;\r
+\r
+ if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ ZeroMem (HttpIo, sizeof (HTTP_IO));\r
+ ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));\r
+\r
+ //\r
+ // Create the HTTP child instance and get the HTTP protocol.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ Controller,\r
+ Image,\r
+ &gEfiHttpServiceBindingProtocolGuid,\r
+ &HttpIo->Handle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ HttpIo->Handle,\r
+ &gEfiHttpProtocolGuid,\r
+ (VOID **) &Http,\r
+ Image,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status) || (Http == NULL)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Init the configuration data and configure the HTTP child.\r
+ //\r
+ HttpIo->Image = Image;\r
+ HttpIo->Controller = Controller;\r
+ HttpIo->IpVersion = IpVersion;\r
+ HttpIo->Http = Http;\r
+ HttpIo->Callback = Callback;\r
+ HttpIo->Context = Context;\r
+\r
+ if (ConfigData != NULL) {\r
+ if (HttpIo->IpVersion == IP_VERSION_4) {\r
+ HttpConfigData.LocalAddressIsIPv6 = FALSE;\r
+ HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion;\r
+ HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;\r
+\r
+ Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;\r
+ Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;\r
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);\r
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);\r
+ HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;\r
+ } else {\r
+ HttpConfigData.LocalAddressIsIPv6 = TRUE;\r
+ HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion;\r
+ HttpConfigData.TimeOutMillisec = ConfigData->Config6.RequestTimeOut;\r
+\r
+ Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;\r
+ IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);\r
+ HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;\r
+ }\r
+\r
+ Status = Http->Configure (Http, &HttpConfigData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create events for variuos asynchronous operations.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ HttpIoNotify,\r
+ &HttpIo->IsTxDone,\r
+ &Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ HttpIo->ReqToken.Event = Event;\r
+ HttpIo->ReqToken.Message = &HttpIo->ReqMessage;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ HttpIoNotify,\r
+ &HttpIo->IsRxDone,\r
+ &Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ HttpIo->RspToken.Event = Event;\r
+ HttpIo->RspToken.Message = &HttpIo->RspMessage;\r
+\r
+ //\r
+ // Create TimeoutEvent for response\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ HttpIo->TimeoutEvent = Event;\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ HttpIoDestroyIo (HttpIo);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Synchronously send a HTTP REQUEST message to the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] Request A pointer to storage such data as URL and HTTP method.\r
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[in] BodyLength Length in bytes of the HTTP body.\r
+ @param[in] Body Body associated with the HTTP request.\r
+\r
+ @retval EFI_SUCCESS The HTTP request is trasmitted.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoSendRequest (\r
+ IN HTTP_IO *HttpIo,\r
+ IN EFI_HTTP_REQUEST_DATA *Request,\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers,\r
+ IN UINTN BodyLength,\r
+ IN VOID *Body\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_PROTOCOL *Http;\r
+\r
+ if (HttpIo == NULL || HttpIo->Http == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpIo->ReqToken.Status = EFI_NOT_READY;\r
+ HttpIo->ReqToken.Message->Data.Request = Request;\r
+ HttpIo->ReqToken.Message->HeaderCount = HeaderCount;\r
+ HttpIo->ReqToken.Message->Headers = Headers;\r
+ HttpIo->ReqToken.Message->BodyLength = BodyLength;\r
+ HttpIo->ReqToken.Message->Body = Body;\r
+\r
+ if (HttpIo->Callback != NULL) {\r
+ Status = HttpIo->Callback (\r
+ HttpIoRequest,\r
+ HttpIo->ReqToken.Message,\r
+ HttpIo->Context\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Queue the request token to HTTP instances.\r
+ //\r
+ Http = HttpIo->Http;\r
+ HttpIo->IsTxDone = FALSE;\r
+ Status = Http->Request (\r
+ Http,\r
+ &HttpIo->ReqToken\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Poll the network until transmit finish.\r
+ //\r
+ while (!HttpIo->IsTxDone) {\r
+ Http->Poll (Http);\r
+ }\r
+\r
+ return HttpIo->ReqToken.Status;\r
+}\r
+\r
+/**\r
+ Synchronously receive a HTTP RESPONSE message from the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).\r
+ FALSE to continue receive the previous response message.\r
+ @param[out] ResponseData Point to a wrapper of the received response data.\r
+\r
+ @retval EFI_SUCCESS The HTTP response is received.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoRecvResponse (\r
+ IN HTTP_IO *HttpIo,\r
+ IN BOOLEAN RecvMsgHeader,\r
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_PROTOCOL *Http;\r
+\r
+ if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Queue the response token to HTTP instances.\r
+ //\r
+ HttpIo->RspToken.Status = EFI_NOT_READY;\r
+ if (RecvMsgHeader) {\r
+ HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;\r
+ } else {\r
+ HttpIo->RspToken.Message->Data.Response = NULL;\r
+ }\r
+ HttpIo->RspToken.Message->HeaderCount = 0;\r
+ HttpIo->RspToken.Message->Headers = NULL;\r
+ HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;\r
+ HttpIo->RspToken.Message->Body = ResponseData->Body;\r
+\r
+ Http = HttpIo->Http;\r
+ HttpIo->IsRxDone = FALSE;\r
+\r
+ //\r
+ // Start the timer, and wait Timeout seconds to receive the header packet.\r
+ //\r
+ Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Http->Response (\r
+ Http,\r
+ &HttpIo->RspToken\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Remove timeout timer from the event list.\r
+ //\r
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Poll the network until receive finish.\r
+ //\r
+ while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent))) {\r
+ Http->Poll (Http);\r
+ }\r
+\r
+ //\r
+ // Remove timeout timer from the event list.\r
+ //\r
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);\r
+\r
+ if (!HttpIo->IsRxDone) {\r
+ //\r
+ // Timeout occurs, cancel the response token.\r
+ //\r
+ Http->Cancel (Http, &HttpIo->RspToken);\r
+\r
+ Status = EFI_TIMEOUT;\r
+\r
+ return Status;\r
+ } else {\r
+ HttpIo->IsRxDone = FALSE;\r
+ }\r
+\r
+ if ((HttpIo->Callback != NULL) &&\r
+ (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) {\r
+ Status = HttpIo->Callback (\r
+ HttpIoResponse,\r
+ HttpIo->RspToken.Message,\r
+ HttpIo->Context\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Store the received data into the wrapper.\r
+ //\r
+ ResponseData->Status = HttpIo->RspToken.Status;\r
+ ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;\r
+ ResponseData->Headers = HttpIo->RspToken.Message->Headers;\r
+ ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get the value of the content length if there is a "Content-Length" header.\r
+\r
+ @param[in] HeaderCount Number of HTTP header structures in Headers.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[out] ContentLength Pointer to save the value of the content length.\r
+\r
+ @retval EFI_SUCCESS Successfully get the content length.\r
+ @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoGetContentLength (\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers,\r
+ OUT UINTN *ContentLength\r
+ )\r
+{\r
+ EFI_HTTP_HEADER *Header;\r
+\r
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);\r
+ if (Header == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);\r
+}\r
+/**\r
+ Send HTTP request in chunks.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] SendChunkProcess Pointer to current chunk process status.\r
+ @param[in] RequestMessage Request to send.\r
+\r
+ @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.\r
+ @retval Other Other errors.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoSendChunkedTransfer (\r
+ IN HTTP_IO *HttpIo,\r
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,\r
+ IN EFI_HTTP_MESSAGE *RequestMessage\r
+)\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_HEADER *NewHeaders;\r
+ EFI_HTTP_HEADER *ContentLengthHeader;\r
+ UINTN AddNewHeader;\r
+ UINTN HeaderCount;\r
+ CHAR8 *MessageBody;\r
+ UINTN MessageBodyLength;\r
+ UINTN ChunkLength;\r
+ CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN];\r
+ EFI_HTTP_REQUEST_DATA *SentRequestData;\r
+\r
+ AddNewHeader = 0;\r
+ NewHeaders = NULL;\r
+ MessageBody = NULL;\r
+ ContentLengthHeader = NULL;\r
+ MessageBodyLength = 0;\r
+\r
+ switch (*SendChunkProcess) {\r
+ case HttpIoSendChunkHeaderZeroContent:\r
+ ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH);\r
+ if (ContentLengthHeader == NULL) {\r
+ AddNewHeader = 1;\r
+ }\r
+\r
+ NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + AddNewHeader) * sizeof(EFI_HTTP_HEADER));\r
+ CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));\r
+ if (AddNewHeader == 0) {\r
+ //\r
+ // Override content-length to Transfer-Encoding.\r
+ //\r
+ ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);\r
+ ContentLengthHeader->FieldName = NULL;\r
+ ContentLengthHeader->FieldValue = NULL;\r
+ } else {\r
+ ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount;\r
+ }\r
+ HttpSetFieldNameAndValue (ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);\r
+ HeaderCount = RequestMessage->HeaderCount + AddNewHeader;\r
+ MessageBodyLength = 0;\r
+ MessageBody = NULL;\r
+ SentRequestData = RequestMessage->Data.Request;\r
+ break;\r
+\r
+ case HttpIoSendChunkContent:\r
+ HeaderCount = 0;\r
+ NewHeaders = NULL;\r
+ SentRequestData = NULL;\r
+ if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {\r
+ MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;\r
+ } else {\r
+ MessageBodyLength = RequestMessage->BodyLength;\r
+ }\r
+ AsciiSPrint (\r
+ ChunkLengthStr,\r
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,\r
+ "%x%c%c",\r
+ MessageBodyLength,\r
+ CHUNKED_TRANSFER_CODING_CR,\r
+ CHUNKED_TRANSFER_CODING_LF\r
+ );\r
+ ChunkLength = AsciiStrLen (ChunkLengthStr);\r
+ MessageBody = AllocatePool (ChunkLength + MessageBodyLength + 2);\r
+ if (MessageBody == NULL) {\r
+ DEBUG((DEBUG_ERROR, "Not enough memory for chunk transfer\n"));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Build up the chunk transfer paylaod.\r
+ //\r
+ CopyMem (MessageBody, ChunkLengthStr, ChunkLength);\r
+ CopyMem (MessageBody + ChunkLength, RequestMessage->Body, MessageBodyLength);\r
+ *(MessageBody + ChunkLength + MessageBodyLength) = CHUNKED_TRANSFER_CODING_CR;\r
+ *(MessageBody + ChunkLength + MessageBodyLength + 1) = CHUNKED_TRANSFER_CODING_LF;\r
+ //\r
+ // Change variables for the next chunk trasnfer.\r
+ //\r
+ RequestMessage->BodyLength -= MessageBodyLength;\r
+ RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength);\r
+ MessageBodyLength += (ChunkLength + 2);\r
+ if (RequestMessage->BodyLength == 0) {\r
+ *SendChunkProcess = HttpIoSendChunkEndChunk;\r
+ }\r
+ break;\r
+\r
+ case HttpIoSendChunkEndChunk:\r
+ HeaderCount = 0;\r
+ NewHeaders = NULL;\r
+ SentRequestData = NULL;\r
+ AsciiSPrint (\r
+ ChunkLengthStr,\r
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,\r
+ "0%c%c%c%c",\r
+ CHUNKED_TRANSFER_CODING_CR,\r
+ CHUNKED_TRANSFER_CODING_LF,\r
+ CHUNKED_TRANSFER_CODING_CR,\r
+ CHUNKED_TRANSFER_CODING_LF\r
+ );\r
+ MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr));\r
+ if (MessageBody == NULL) {\r
+ DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n"));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));\r
+ MessageBodyLength = AsciiStrLen (ChunkLengthStr);\r
+ *SendChunkProcess = HttpIoSendChunkFinish;\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = HttpIoSendRequest (\r
+ HttpIo,\r
+ SentRequestData,\r
+ HeaderCount,\r
+ NewHeaders,\r
+ MessageBodyLength,\r
+ MessageBody\r
+ );\r
+ if (ContentLengthHeader != NULL) {\r
+ if (ContentLengthHeader->FieldName != NULL) {\r
+ FreePool (ContentLengthHeader->FieldName);\r
+ }\r
+ if (ContentLengthHeader->FieldValue != NULL) {\r
+ FreePool (ContentLengthHeader->FieldValue);\r
+ }\r
+ }\r
+ if (NewHeaders != NULL) {\r
+ FreePool (NewHeaders);\r
+ }\r
+ if (MessageBody != NULL) {\r
+ FreePool (MessageBody);\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Synchronously receive a HTTP RESPONSE message from the server.\r
+\r
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
+ @param[in] HeaderCount Number of headers in Headers.\r
+ @param[in] Headers Array containing list of HTTP headers.\r
+ @param[out] ChunkListHead A pointer to receive list head\r
+ of chunked data. Caller has to\r
+ release memory of ChunkListHead\r
+ and all list entries.\r
+ @param[out] ContentLength Total content length\r
+\r
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.\r
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval EFI_INVALID_PARAMETER Improper parameters.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpIoGetChunkedTransferContent (\r
+ IN HTTP_IO *HttpIo,\r
+ IN UINTN HeaderCount,\r
+ IN EFI_HTTP_HEADER *Headers,\r
+ OUT LIST_ENTRY **ChunkListHead,\r
+ OUT UINTN *ContentLength\r
+ )\r
+{\r
+ EFI_HTTP_HEADER *Header;\r
+ CHAR8 ChunkSizeAscii [256];\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ HTTP_IO_RESPONSE_DATA ResponseData;\r
+ UINTN TotalLength;\r
+ UINTN MaxTotalLength;\r
+ LIST_ENTRY *HttpChunks;\r
+ HTTP_IO_CHUNKS *ThisChunk;\r
+ LIST_ENTRY *ThisListEntry;\r
+\r
+ if (ChunkListHead == NULL || ContentLength == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *ContentLength = 0;\r
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);\r
+ if (Header == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Loop to get all chunks.\r
+ //\r
+ TotalLength = 0;\r
+ MaxTotalLength = PcdGet32 (PcdMaxHttpChunkTransfer);\r
+ HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));\r
+ if (HttpChunks == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ExitDeleteChunks;\r
+ }\r
+ InitializeListHead (HttpChunks);\r
+ DEBUG ((DEBUG_INFO, " Chunked transfer\n"));\r
+ while (TRUE) {\r
+ ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));\r
+ ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;\r
+ ResponseData.Body = ChunkSizeAscii;\r
+ Status = HttpIoRecvResponse (\r
+ HttpIo,\r
+ FALSE,\r
+ &ResponseData\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ExitDeleteChunks;\r
+ }\r
+ //\r
+ // Decoding Chunked Transfer Coding.\r
+ // Only decode chunk-size and last chunk.\r
+ //\r
+ DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode));\r
+ //\r
+ // Break if this is last chunk.\r
+ //\r
+ if (ChunkSizeAscii [0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK) {\r
+ //\r
+ // Check if this is a valid Last-Chunk.\r
+ //\r
+ if ((ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_CR) ||\r
+ (ChunkSizeAscii [2] != CHUNKED_TRANSFER_CODING_LF)\r
+ ) {\r
+ DEBUG ((DEBUG_ERROR, " This is an invalid Last-chunk\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ExitDeleteChunks;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ DEBUG ((DEBUG_INFO, " Last-chunk\n"));\r
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));\r
+ if (ThisChunk == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ExitDeleteChunks;\r
+ }\r
+\r
+ InitializeListHead (&ThisChunk->NextChunk);\r
+ ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF.\r
+ ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);\r
+ if (ThisChunk->Data == NULL) {\r
+ FreePool ((UINT8 *)ThisChunk);\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ExitDeleteChunks;\r
+ }\r
+ CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length);\r
+ TotalLength += ThisChunk->Length;\r
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Get the chunk length\r
+ //\r
+ Index = 0;\r
+ while ((ChunkSizeAscii [Index] != CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) &&\r
+ (ChunkSizeAscii [Index] != (CHAR8)CHUNKED_TRANSFER_CODING_CR) &&\r
+ (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH)) {\r
+ Index ++;\r
+ }\r
+\r
+ if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {\r
+ Status = EFI_NOT_FOUND;\r
+ goto ExitDeleteChunks;\r
+ }\r
+ ChunkSizeAscii[Index] = 0;\r
+ AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);\r
+ DEBUG ((DEBUG_INFO, " Length of this chunk %d\n", *ContentLength));\r
+ //\r
+ // Receive the data;\r
+ //\r
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));\r
+ if (ThisChunk == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ExitDeleteChunks;\r
+ }\r
+ ResponseData.BodyLength = *ContentLength;\r
+ ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);\r
+ if (ResponseData.Body == NULL) {\r
+ FreePool (ThisChunk);\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ExitDeleteChunks;\r
+ }\r
+ InitializeListHead (&ThisChunk->NextChunk);\r
+ ThisChunk->Length = *ContentLength;\r
+ ThisChunk->Data = ResponseData.Body;\r
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);\r
+ Status = HttpIoRecvResponse (\r
+ HttpIo,\r
+ FALSE,\r
+ &ResponseData\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ExitDeleteChunks;\r
+ }\r
+ //\r
+ // Read CRLF\r
+ //\r
+ ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));\r
+ ResponseData.BodyLength = 2;\r
+ ResponseData.Body = ChunkSizeAscii;\r
+ Status = HttpIoRecvResponse (\r
+ HttpIo,\r
+ FALSE,\r
+ &ResponseData\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ExitDeleteChunks;\r
+ }\r
+ //\r
+ // Verify the end of chunk payload.\r
+ //\r
+ if ((ChunkSizeAscii [0] != CHUNKED_TRANSFER_CODING_CR) ||\r
+ (ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_LF)\r
+ ) {\r
+ DEBUG ((DEBUG_ERROR, " This is an invalid End-of-chunk notation.\n"));\r
+ goto ExitDeleteChunks;\r
+ }\r
+ TotalLength += *ContentLength;\r
+ if (TotalLength > MaxTotalLength) {\r
+ DEBUG ((DEBUG_ERROR, " Total chunk transfer payload exceeds the size defined by PcdMaxHttpChunkTransfer.\n"));\r
+ goto ExitDeleteChunks;\r
+ }\r
+ }\r
+\r
+ *ContentLength = TotalLength;\r
+ *ChunkListHead = HttpChunks;\r
+ DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n", TotalLength));\r
+ return EFI_SUCCESS;\r
+\r
+ExitDeleteChunks:\r
+ if (HttpChunks != NULL) {\r
+ while (!IsListEmpty(HttpChunks)) {\r
+ ThisListEntry = GetFirstNode (HttpChunks);\r
+ RemoveEntryList (ThisListEntry);\r
+ ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry;\r
+ if (ThisChunk->Data != NULL) {\r
+ FreePool (ThisChunk->Data);\r
+ }\r
+ FreePool(ThisListEntry);\r
+ }\r
+ FreePool (HttpChunks);\r
+ }\r
+ return Status;\r
+}\r