]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpBootDxe/HttpBootClient.c
NetworkPkg: Fix some typos in Http boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
index 5669c5f37ce1f511879fe117d34f8420cf2a12d3..f0817e92e21e38b3d7ef4ccf606947ce2efbf605 100644 (file)
@@ -54,14 +54,27 @@ HttpBootUpdateDevicePath (
     Node->Ipv4.StaticIpAddress = FALSE;\r
     CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));\r
     CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
-    \r
-    TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
-    FreePool (Node);\r
-    if (TmpDevicePath == NULL) {\r
+  } else {\r
+    Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
+    if (Node == NULL) {\r
       return EFI_OUT_OF_RESOURCES;\r
     }\r
-  } else {\r
-    ASSERT (FALSE);\r
+    Node->Ipv6.Header.Type     = MESSAGING_DEVICE_PATH;\r
+    Node->Ipv6.Header.SubType  = MSG_IPv6_DP;\r
+    SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));\r
+    Node->Ipv6.PrefixLength    = IP6_PREFIX_LENGTH;\r
+    Node->Ipv6.RemotePort      = Private->Port;\r
+    Node->Ipv6.Protocol        = EFI_IP_PROTO_TCP; \r
+    Node->Ipv6.IpAddressOrigin = 0;\r
+    CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+    CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+    CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+  \r
+  TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+  FreePool (Node);\r
+  if (TmpDevicePath == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
   //\r
@@ -85,21 +98,39 @@ HttpBootUpdateDevicePath (
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  //\r
-  // Reinstall the device path protocol of the child handle.\r
-  //\r
-  Status = gBS->ReinstallProtocolInterface (\r
-                  Private->ChildHandle,\r
-                  &gEfiDevicePathProtocolGuid,\r
-                  Private->DevicePath,\r
-                  NewDevicePath\r
-                  );\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  if (!Private->UsingIpv6) {\r
+    //\r
+    // Reinstall the device path protocol of the child handle.\r
+    //\r
+    Status = gBS->ReinstallProtocolInterface (\r
+                    Private->Ip4Nic->Controller,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    Private->Ip4Nic->DevicePath,\r
+                    NewDevicePath\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    \r
+    FreePool (Private->Ip4Nic->DevicePath);\r
+    Private->Ip4Nic->DevicePath = NewDevicePath;\r
+  } else {\r
+    //\r
+    // Reinstall the device path protocol of the child handle.\r
+    //\r
+    Status = gBS->ReinstallProtocolInterface (\r
+                    Private->Ip6Nic->Controller,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    Private->Ip6Nic->DevicePath,\r
+                    NewDevicePath\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    FreePool (Private->Ip6Nic->DevicePath);\r
+    Private->Ip6Nic->DevicePath = NewDevicePath;\r
   }\r
   \r
-  FreePool (Private->DevicePath);\r
-  Private->DevicePath = NewDevicePath;\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -113,7 +144,7 @@ HttpBootUpdateDevicePath (
 \r
 **/\r
 EFI_STATUS\r
-HttpBootExtractUriInfo (\r
+HttpBootDhcp4ExtractUriInfo (\r
   IN     HTTP_BOOT_PRIVATE_DATA   *Private\r
   )\r
 {\r
@@ -192,6 +223,159 @@ HttpBootExtractUriInfo (
   return Status;\r
 }\r
 \r
+/**\r
+  Parse the boot file URI information from the selected Dhcp6 offer packet.\r
+\r
+  @param[in]    Private        The pointer to the driver's private data.\r
+\r
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDhcp6ExtractUriInfo (\r
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private\r
+  )\r
+{\r
+  HTTP_BOOT_DHCP6_PACKET_CACHE    *SelectOffer;\r
+  HTTP_BOOT_DHCP6_PACKET_CACHE    *HttpOffer;\r
+  UINT32                          SelectIndex;\r
+  UINT32                          ProxyIndex;\r
+  EFI_DHCP6_PACKET_OPTION         *Option;\r
+  EFI_IPv6_ADDRESS                IpAddr;\r
+  CHAR8                           *HostName;\r
+  CHAR16                          *HostNameStr;\r
+  EFI_STATUS                      Status;\r
+\r
+  ASSERT (Private != NULL);\r
+  ASSERT (Private->SelectIndex != 0);\r
+  SelectIndex = Private->SelectIndex - 1;\r
+  ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);\r
+\r
+  Status   = EFI_SUCCESS;\r
+  HostName = NULL;\r
+  //\r
+  // SelectOffer contains the IP address configuration and name server configuration.\r
+  // HttpOffer contains the boot file URL.\r
+  //\r
+  SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
+  if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {\r
+    HttpOffer = SelectOffer;\r
+  } else {\r
+    ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
+    ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+    HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;\r
+  }\r
+\r
+  //\r
+  //  Set the Local station address to IP layer.\r
+  //\r
+  Status = HttpBootSetIp6Address (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  \r
+  //\r
+  // Configure the default DNS server if server assigned.\r
+  //\r
+  if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {\r
+    Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
+    ASSERT (Option != NULL);\r
+    Status = HttpBootSetIp6Dns (\r
+               Private,\r
+               HTONS (Option->OpLen),\r
+               Option->Data\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+  \r
+  //\r
+  // Extract the HTTP server Ip frome URL. This is used to Check route table \r
+  // whether can send message to HTTP Server Ip through the GateWay.\r
+  //\r
+  Status = HttpUrlGetIp6 (\r
+             (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+             HttpOffer->UriParser,\r
+             &IpAddr\r
+             );\r
+  \r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // The Http server address is expressed by Name Ip, so perform DNS resolution\r
+    //\r
+    Status = HttpUrlGetHostName (\r
+               (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+               HttpOffer->UriParser,\r
+               &HostName\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    \r
+    HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));\r
+    if (HostNameStr == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Error;\r
+    }\r
+    \r
+    AsciiStrToUnicodeStr (HostName, HostNameStr);\r
+    Status = HttpBootDns (Private, HostNameStr, &IpAddr);\r
+    FreePool (HostNameStr);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }  \r
+  } \r
+  \r
+  CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));  \r
+    \r
+  //\r
+  // register the IPv6 gateway address to the network device.\r
+  //\r
+  Status = HttpBootSetIp6Gateway (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  \r
+  //\r
+  // Extract the port from URL, and use default HTTP port 80 if not provided.\r
+  //\r
+  Status = HttpUrlGetPort (\r
+             (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+             HttpOffer->UriParser,\r
+             &Private->Port\r
+             );\r
+  if (EFI_ERROR (Status) || Private->Port == 0) {\r
+    Private->Port = 80;\r
+  }\r
+  \r
+  //\r
+  // Record the URI of boot file from the selected HTTP offer.\r
+  //\r
+  Private->BootFileUriParser = HttpOffer->UriParser;\r
+  Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;\r
+\r
+  \r
+  //\r
+  // All boot informations are valid here.\r
+  //\r
+  AsciiPrint ("\n  URI: %a", Private->BootFileUri);\r
+  //\r
+  // Update the device path to include the IP and boot URI information.\r
+  //\r
+  Status = HttpBootUpdateDevicePath (Private);\r
+\r
+Error:\r
+  \r
+  if (HostName != NULL) {\r
+    FreePool (HostName);\r
+  }\r
+    \r
+  return Status;\r
+}\r
+\r
+\r
 /**\r
   Discover all the boot information for boot file.\r
 \r
@@ -218,9 +402,9 @@ HttpBootDiscoverBootInfo (
   }\r
 \r
   if (!Private->UsingIpv6) {\r
-    Status = HttpBootExtractUriInfo (Private);\r
+    Status = HttpBootDhcp4ExtractUriInfo (Private);\r
   } else {\r
-    ASSERT (FALSE);\r
+    Status = HttpBootDhcp6ExtractUriInfo (Private);\r
   }\r
 \r
   return Status;\r
@@ -247,12 +431,14 @@ HttpBootCreateHttpIo (
 \r
   ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
   if (!Private->UsingIpv6) {\r
-    ConfigData.Config4.HttpVersion = HttpVersion11;\r
+    ConfigData.Config4.HttpVersion    = HttpVersion11;\r
     ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
     IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);\r
     IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);\r
   } else {\r
-    ASSERT (FALSE);\r
+    ConfigData.Config6.HttpVersion    = HttpVersion11;\r
+    ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
+    IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);\r
   }\r
 \r
   Status = HttpIoCreateIo (\r
@@ -270,81 +456,6 @@ HttpBootCreateHttpIo (
   return EFI_SUCCESS;\r
 }\r
 \r
-/**\r
-  Get the file content from cached data.\r
-\r
-  @param[in]          Private         The pointer to the driver's private data.\r
-  @param[in]          Uri             Uri of the file to be retrieved from cache.\r
-  @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return\r
-                                      code of EFI_SUCCESS, the amount of data transferred to\r
-                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
-                                      the size of Buffer required to retrieve the requested file.\r
-  @param[out]         Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,\r
-                                      then the size of the requested file is returned in\r
-                                      BufferSize.\r
-\r
-  @retval EFI_SUCCESS          Successfully created.\r
-  @retval Others               Failed to create HttpIo.\r
-\r
-**/\r
-EFI_STATUS\r
-HttpBootGetFileFromCache (\r
-  IN     HTTP_BOOT_PRIVATE_DATA   *Private,\r
-  IN     CHAR16                   *Uri,\r
-  IN OUT UINTN                    *BufferSize,\r
-     OUT UINT8                    *Buffer\r
-  )\r
-{\r
-  LIST_ENTRY                  *Entry;\r
-  LIST_ENTRY                  *Entry2;\r
-  HTTP_BOOT_CACHE_CONTENT     *Cache;\r
-  HTTP_BOOT_ENTITY_DATA       *EntityData;\r
-  UINTN                       CopyedSize;\r
-  \r
-  if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
-\r
-  NET_LIST_FOR_EACH (Entry, &Private->CacheList) {\r
-    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
-    //\r
-    // Compare the URI to see whether we already have a cache for this file.\r
-    //\r
-    if ((Cache->RequestData != NULL) &&\r
-        (Cache->RequestData->Url != NULL) &&\r
-        (StrCmp (Uri, Cache->RequestData->Url) == 0)) \r
-    {\r
-      //\r
-      // Hit cache, check buffer size.\r
-      //\r
-      if (*BufferSize < Cache->EntityLength) {\r
-        *BufferSize = Cache->EntityLength;\r
-        return EFI_BUFFER_TOO_SMALL;\r
-      }\r
-\r
-      //\r
-      // Fill data to buffer.\r
-      //\r
-      CopyedSize = 0;\r
-      NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {\r
-        EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);\r
-        if (*BufferSize > CopyedSize) {\r
-          CopyMem (\r
-            Buffer + CopyedSize,\r
-            EntityData->DataStart,\r
-            MIN (EntityData->DataLength, *BufferSize - CopyedSize)\r
-            );\r
-          CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);\r
-        }\r
-      }\r
-      *BufferSize = CopyedSize;\r
-      return EFI_SUCCESS;\r
-    }\r
-  }\r
-\r
-  return EFI_NOT_FOUND;\r
-}\r
-\r
 /**\r
   Release all the resource of a cache item.\r
 \r
@@ -423,6 +534,91 @@ HttpBootFreeCacheList (
   }\r
 }\r
 \r
+/**\r
+  Get the file content from cached data.\r
+\r
+  @param[in]          Private         The pointer to the driver's private data.\r
+  @param[in]          Uri             Uri of the file to be retrieved from cache.\r
+  @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return\r
+                                      code of EFI_SUCCESS, the amount of data transferred to\r
+                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
+                                      the size of Buffer required to retrieve the requested file.\r
+  @param[out]         Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,\r
+                                      then the size of the requested file is returned in\r
+                                      BufferSize.\r
+\r
+  @retval EFI_SUCCESS          Successfully created.\r
+  @retval Others               Failed to create HttpIo.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootGetFileFromCache (\r
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private,\r
+  IN     CHAR16                   *Uri,\r
+  IN OUT UINTN                    *BufferSize,\r
+     OUT UINT8                    *Buffer\r
+  )\r
+{\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *Entry2;\r
+  HTTP_BOOT_CACHE_CONTENT     *Cache;\r
+  HTTP_BOOT_ENTITY_DATA       *EntityData;\r
+  UINTN                       CopyedSize;\r
+  \r
+  if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Search file in the cache list, the cache entry will be released upon a successful\r
+  // match.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &Private->CacheList) {\r
+    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
+    //\r
+    // Compare the URI to see whether we already have a cache for this file.\r
+    //\r
+    if ((Cache->RequestData != NULL) &&\r
+        (Cache->RequestData->Url != NULL) &&\r
+        (StrCmp (Uri, Cache->RequestData->Url) == 0)) \r
+    {\r
+      //\r
+      // Hit cache, check buffer size.\r
+      //\r
+      if (*BufferSize < Cache->EntityLength) {\r
+        *BufferSize = Cache->EntityLength;\r
+        return EFI_BUFFER_TOO_SMALL;\r
+      }\r
+\r
+      //\r
+      // Fill data to buffer.\r
+      //\r
+      CopyedSize = 0;\r
+      NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {\r
+        EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);\r
+        if (*BufferSize > CopyedSize) {\r
+          CopyMem (\r
+            Buffer + CopyedSize,\r
+            EntityData->DataStart,\r
+            MIN (EntityData->DataLength, *BufferSize - CopyedSize)\r
+            );\r
+          CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);\r
+        }\r
+      }\r
+      *BufferSize = CopyedSize;\r
+\r
+      //\r
+      // On success, free the cached data to release the memory resource.\r
+      //\r
+      RemoveEntryList (&Cache->Link);\r
+      HttpBootFreeCache (Cache);\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
 /**\r
   A callback function to intercept events during message parser.\r
 \r
@@ -523,8 +719,8 @@ HttpBootGetBootFile (
   EFI_STATUS                 Status;\r
   CHAR8                      *HostName;\r
   EFI_HTTP_REQUEST_DATA      *RequestData;\r
-  HTTP_IO_RESOPNSE_DATA      *ResponseData;\r
-  HTTP_IO_RESOPNSE_DATA      ResponseBody;\r
+  HTTP_IO_RESPONSE_DATA      *ResponseData;\r
+  HTTP_IO_RESPONSE_DATA      ResponseBody;\r
   HTTP_IO                    *HttpIo;\r
   HTTP_IO_HEADER             *HttpIoHeader;\r
   VOID                       *Parser;\r
@@ -533,6 +729,8 @@ HttpBootGetBootFile (
   HTTP_BOOT_CACHE_CONTENT    *Cache;\r
   UINT8                      *Block;\r
   CHAR16                     *Url;\r
+  BOOLEAN                    IdentityMode;\r
+  UINTN                      ReceivedSize;\r
   \r
   ASSERT (Private != NULL);\r
   ASSERT (Private->HttpCreated);\r
@@ -686,7 +884,7 @@ HttpBootGetBootFile (
   //\r
   // 3.1 First step, use zero BodyLength to only receive the response headers.\r
   //\r
-  ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));\r
+  ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));\r
   if (ResponseData == NULL) {\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto ERROR_4;\r
@@ -735,51 +933,94 @@ HttpBootGetBootFile (
   //\r
   Block = NULL;\r
   if (!HeaderOnly) {\r
-    ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));\r
-    while (!HttpIsMessageComplete (Parser)) {\r
+    //\r
+    // 3.4.1, check whether we are in identity transfer-coding.\r
+    //\r
+    ContentLength = 0;\r
+    Status = HttpGetEntityLength (Parser, &ContentLength);\r
+    if (!EFI_ERROR (Status)) {\r
+      IdentityMode = TRUE;\r
+    } else {\r
+      IdentityMode = FALSE;\r
+    }\r
+\r
+    //\r
+    // 3.4.2, start the message-body download, the identity and chunked transfer-coding\r
+    // is handled in different path here.\r
+    //\r
+    ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));\r
+    if (IdentityMode) {\r
       //\r
-      // Allocate a block to hold the message-body, if caller doesn't provide\r
-      // a buffer, the block will be cached and we will allocate a new one here.\r
+      // In identity transfer-coding there is no need to parse the message body,\r
+      // just download the message body to the user provided buffer directly.\r
       //\r
-      if (Block == NULL || Context.BufferSize == 0) {\r
-        Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
-        if (Block == NULL) {\r
-          Status = EFI_OUT_OF_RESOURCES;\r
+      ReceivedSize = 0;\r
+      while (ReceivedSize < ContentLength) {\r
+        ResponseBody.Body       = (CHAR8*) Buffer + ReceivedSize;\r
+        ResponseBody.BodyLength = *BufferSize - ReceivedSize;\r
+        Status = HttpIoRecvResponse (\r
+                   &Private->HttpIo,\r
+                   FALSE,\r
+                   &ResponseBody\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
           goto ERROR_6;\r
         }\r
-        Context.NewBlock = TRUE;\r
-        Context.Block = Block;\r
-      } else {\r
-        Context.NewBlock = FALSE;\r
+        ReceivedSize += ResponseBody.BodyLength;\r
       }\r
-\r
-      ResponseBody.Body       = (CHAR8*) Block;\r
-      ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
-      Status = HttpIoRecvResponse (\r
-                 &Private->HttpIo,\r
-                 FALSE,\r
-                 &ResponseBody\r
-                 );\r
-      if (EFI_ERROR (Status)) {\r
-        goto ERROR_6;\r
-      }\r
-\r
+    } else {\r
       //\r
-      // Parse the new received block of the message-body, the block will be saved in cache.\r
+      // In "chunked" transfer-coding mode, so we need to parse the received\r
+      // data to get the real entity content.\r
       //\r
-      Status = HttpParseMessageBody (\r
-                 Parser,\r
-                 ResponseBody.BodyLength,\r
-                 ResponseBody.Body\r
-                 );\r
-      if (EFI_ERROR (Status)) {\r
-        goto ERROR_6;\r
+      Block = NULL;\r
+      while (!HttpIsMessageComplete (Parser)) {\r
+        //\r
+        // Allocate a buffer in Block to hold the message-body.\r
+        // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().\r
+        // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before\r
+        // every HttpIoRecvResponse().\r
+        //\r
+        if (Block == NULL || Context.BufferSize == 0) {\r
+          Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
+          if (Block == NULL) {\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            goto ERROR_6;\r
+          }\r
+          Context.NewBlock = TRUE;\r
+          Context.Block = Block;\r
+        } else {\r
+          Context.NewBlock = FALSE;\r
+        }\r
+\r
+        ResponseBody.Body       = (CHAR8*) Block;\r
+        ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
+        Status = HttpIoRecvResponse (\r
+                   &Private->HttpIo,\r
+                   FALSE,\r
+                   &ResponseBody\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          goto ERROR_6;\r
+        }\r
+\r
+        //\r
+        // Parse the new received block of the message-body, the block will be saved in cache.\r
+        //\r
+        Status = HttpParseMessageBody (\r
+                   Parser,\r
+                   ResponseBody.BodyLength,\r
+                   ResponseBody.Body\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          goto ERROR_6;\r
+        }\r
       }\r
     }\r
   }\r
-  \r
+\r
   //\r
-  // 3.5 Message-body receive & parse is completed, get the file size.\r
+  // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.\r
   //\r
   Status = HttpGetEntityLength (Parser, &ContentLength);\r
   if (EFI_ERROR (Status)) {\r