]> git.proxmox.com Git - mirror_edk2.git/commitdiff
NetworkPkg/Library: Implementation of Http IO Helper Library
authorAbner Chang <abner.chang@hpe.com>
Tue, 3 Nov 2020 05:26:49 +0000 (13:26 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Fri, 4 Dec 2020 11:45:07 +0000 (11:45 +0000)
Add HTTP IO helper library which could be used by HTTP applications
such as HTTP Boot, Redfish HTTP REST EX driver instance and etc.

Signed-off-by: Abner Chang <abner.chang@hpe.com>
Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>
Cc: Jiaxin Wu <jiaxin.wu@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Cc: Nickle Wang <nickle.wang@hpe.com>
Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
NetworkPkg/Include/Library/HttpIoLib.h [new file with mode: 0644]
NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c [new file with mode: 0644]
NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf [new file with mode: 0644]
NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni [new file with mode: 0644]

diff --git a/NetworkPkg/Include/Library/HttpIoLib.h b/NetworkPkg/Include/Library/HttpIoLib.h
new file mode 100644 (file)
index 0000000..8f3804c
--- /dev/null
@@ -0,0 +1,328 @@
+/** @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
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
new file mode 100644 (file)
index 0000000..67583fb
--- /dev/null
@@ -0,0 +1,853 @@
+/** @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
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
new file mode 100644 (file)
index 0000000..25f5eb2
--- /dev/null
@@ -0,0 +1,45 @@
+## @file\r
+#  This library instance provides HTTP IO helper functions.\r
+#\r
+#  (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>\r
+#  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x0001001b\r
+  BASE_NAME                      = DxeHttpIoLib\r
+  MODULE_UNI_FILE                = DxeHttpIoLib.uni\r
+  FILE_GUID                      = 50B198F8-7986-4F51-A857-CFE4643D59F3\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = HttpIoLib| DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 EBC ARM AARM64 RISCV64\r
+#\r
+\r
+[Sources]\r
+  DxeHttpIoLib.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  NetworkPkg/NetworkPkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  DebugLib\r
+  DpcLib\r
+  MemoryAllocationLib\r
+  PrintLib\r
+  UefiBootServicesTableLib\r
+\r
+[Protocols]\r
+  gEfiHttpProtocolGuid             ## SOMETIMES_CONSUMES\r
+\r
+[Pcd]\r
+  gEfiNetworkPkgTokenSpaceGuid.PcdMaxHttpChunkTransfer  ## SOMETIMES_CONSUMES\r
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
new file mode 100644 (file)
index 0000000..f30c572
--- /dev/null
@@ -0,0 +1,12 @@
+// /** @file\r
+// The library instance provides HTTP IO helper functions.\r
+//\r
+// (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>\r
+// SPDX-License-Identifier: BSD-2-Clause-Patent\r
+//\r
+// **/\r
+\r
+\r
+#string STR_MODULE_ABSTRACT             #language en-US "HTTP IO Helper Library"\r
+\r
+#string STR_MODULE_DESCRIPTION          #language en-US "The library instance provides HTTP IO helper functions."\r