]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpBootDxe/HttpBootClient.c
NetworkPkg:Enable Http Boot over Ipv6 stack
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
index 2bf28c2c4d8918db98ddba4eb2afacc2122011a7..b81b03c96070400db493e148f53ebfcb1e4cb2a4 100644 (file)
-/** @file
-  Implementation of the boot file download function.
-
-Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
-This program and the accompanying materials are licensed and made available under 
-the terms and conditions of the BSD License that accompanies this distribution.  
-The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php.                                          
-    
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-**/
-
-#include "HttpBootDxe.h"
-
-/**
-  Update the IP and URL device path node to include the boot resource information.
-
-  @param[in]    Private            The pointer to the driver's private data.
-
-  @retval EFI_SUCCESS              Device patch successfully updated.
-  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
-  @retval Others                   Unexpected error happened.
-  
-**/
-EFI_STATUS
-HttpBootUpdateDevicePath (
-  IN   HTTP_BOOT_PRIVATE_DATA   *Private
-  )
-{
-  EFI_DEV_PATH               *Node;
-  EFI_DEVICE_PATH_PROTOCOL   *TmpDevicePath;
-  EFI_DEVICE_PATH_PROTOCOL   *NewDevicePath;
-  UINTN                      Length;
-  EFI_STATUS                 Status;
-
-  TmpDevicePath = NULL;
-  
-  //
-  // Update the IP node with DHCP assigned information.
-  //
-  if (!Private->UsingIpv6) {
-    Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
-    if (Node == NULL) {
-      return EFI_OUT_OF_RESOURCES;
-    }
-    Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;
-    Node->Ipv4.Header.SubType = MSG_IPv4_DP;
-    SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
-    CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
-    Node->Ipv4.RemotePort      = Private->Port;
-    Node->Ipv4.Protocol        = EFI_IP_PROTO_TCP;
-    Node->Ipv4.StaticIpAddress = FALSE;
-    CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
-    CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
-    
-    TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
-    FreePool (Node);
-    if (TmpDevicePath == NULL) {
-      return EFI_OUT_OF_RESOURCES;
-    }
-  } else {
-    ASSERT (FALSE);
-  }
-
-  //
-  // Update the URI node with the boot file URI.
-  //
-  Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
-  Node = AllocatePool (Length);
-  if (Node == NULL) {
-    FreePool (TmpDevicePath);
-    return EFI_OUT_OF_RESOURCES;
-  }
-  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
-  Node->DevPath.SubType = MSG_URI_DP;
-  SetDevicePathNodeLength (Node, Length);
-  CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
-  
-  NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
-  FreePool (Node);
-  FreePool (TmpDevicePath);
-  if (NewDevicePath == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  //
-  // Reinstall the device path protocol of the child handle.
-  //
-  Status = gBS->ReinstallProtocolInterface (
-                  Private->ChildHandle,
-                  &gEfiDevicePathProtocolGuid,
-                  Private->DevicePath,
-                  NewDevicePath
-                  );
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-  
-  FreePool (Private->DevicePath);
-  Private->DevicePath = NewDevicePath;
-  return EFI_SUCCESS;
-}
-
-/**
-  Parse the boot file URI information from the selected Dhcp4 offer packet.
-
-  @param[in]    Private        The pointer to the driver's private data.
-
-  @retval EFI_SUCCESS          Successfully parsed out all the boot information.
-  @retval Others               Failed to parse out the boot information.
-
-**/
-EFI_STATUS
-HttpBootExtractUriInfo (
-  IN     HTTP_BOOT_PRIVATE_DATA   *Private
-  )
-{
-  HTTP_BOOT_DHCP4_PACKET_CACHE    *SelectOffer;
-  HTTP_BOOT_DHCP4_PACKET_CACHE    *HttpOffer;
-  UINT32                          SelectIndex;
-  UINT32                          ProxyIndex;
-  EFI_DHCP4_PACKET_OPTION         *Option;
-  EFI_STATUS                      Status;
-
-  ASSERT (Private != NULL);
-  ASSERT (Private->SelectIndex != 0);
-  SelectIndex = Private->SelectIndex - 1;
-  ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
-
-  Status = EFI_SUCCESS;
-
-  //
-  // SelectOffer contains the IP address configuration and name server configuration.
-  // HttpOffer contains the boot file URL.
-  //
-  SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
-  if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
-    HttpOffer = SelectOffer;
-  } else {
-    ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
-    ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
-    HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
-  }
-
-  //
-  // Configure the default DNS server if server assigned.
-  //
-  if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {
-    Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
-    ASSERT (Option != NULL);
-    Status = HttpBootRegisterIp4Dns (
-               Private,
-               Option->Length,
-               Option->Data
-               );
-    if (EFI_ERROR (Status)) {
-      return Status;
-    }
-  }
-
-  //
-  // Extract the port from URL, and use default HTTP port 80 if not provided.
-  //
-  Status = HttpUrlGetPort (
-             (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
-             HttpOffer->UriParser,
-             &Private->Port
-             );
-  if (EFI_ERROR (Status) || Private->Port == 0) {
-    Private->Port = 80;
-  }
-  
-  //
-  // Record the URI of boot file from the selected HTTP offer.
-  //
-  Private->BootFileUriParser = HttpOffer->UriParser;
-  Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
-
-  
-  //
-  // All boot informations are valid here.
-  //
-  AsciiPrint ("\n  URI: %a", Private->BootFileUri);
-
-  //
-  // Update the device path to include the IP and boot URI information.
-  //
-  Status = HttpBootUpdateDevicePath (Private);
-
-  return Status;
-}
-
-/**
-  Discover all the boot information for boot file.
-
-  @param[in, out]    Private        The pointer to the driver's private data.
-
-  @retval EFI_SUCCESS          Successfully obtained all the boot information .
-  @retval Others               Failed to retrieve the boot information.
-
-**/
-EFI_STATUS
-HttpBootDiscoverBootInfo (
-  IN OUT HTTP_BOOT_PRIVATE_DATA   *Private
-  )
-{
-  EFI_STATUS              Status;
-  
-  //
-  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
-  // other Http boot information.
-  //
-  Status = HttpBootDhcp (Private);
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  if (!Private->UsingIpv6) {
-    Status = HttpBootExtractUriInfo (Private);
-  } else {
-    ASSERT (FALSE);
-  }
-
-  return Status;
-}
-
-/**
-  Create a HttpIo instance for the file download.
-
-  @param[in]    Private        The pointer to the driver's private data.
-
-  @retval EFI_SUCCESS          Successfully created.
-  @retval Others               Failed to create HttpIo.
-
-**/
-EFI_STATUS
-HttpBootCreateHttpIo (
-  IN     HTTP_BOOT_PRIVATE_DATA       *Private
-  )
-{
-  HTTP_IO_CONFIG_DATA          ConfigData;
-  EFI_STATUS                   Status;
-
-  ASSERT (Private != NULL);
-
-  ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
-  if (!Private->UsingIpv6) {
-    ConfigData.Config4.HttpVersion = HttpVersion11;
-    ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
-    IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
-    IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
-  } else {
-    ASSERT (FALSE);
-  }
-
-  Status = HttpIoCreateIo (
-             Private->Image,
-             Private->Controller,
-             Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
-             &ConfigData,
-             &Private->HttpIo
-             );
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  Private->HttpCreated = TRUE;
-  return EFI_SUCCESS;
-}
-
-/**
-  Get the file content from cached data.
-
-  @param[in]          Private         The pointer to the driver's private data.
-  @param[in]          Uri             Uri of the file to be retrieved from cache.
-  @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
-                                      code of EFI_SUCCESS, the amount of data transferred to
-                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
-                                      the size of Buffer required to retrieve the requested file.
-  @param[out]         Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
-                                      then the size of the requested file is returned in
-                                      BufferSize.
-
-  @retval EFI_SUCCESS          Successfully created.
-  @retval Others               Failed to create HttpIo.
-
-**/
-EFI_STATUS
-HttpBootGetFileFromCache (
-  IN     HTTP_BOOT_PRIVATE_DATA   *Private,
-  IN     CHAR16                   *Uri,
-  IN OUT UINTN                    *BufferSize,
-     OUT UINT8                    *Buffer
-  )
-{
-  LIST_ENTRY                  *Entry;
-  LIST_ENTRY                  *Entry2;
-  HTTP_BOOT_CACHE_CONTENT     *Cache;
-  HTTP_BOOT_ENTITY_DATA       *EntityData;
-  UINTN                       CopyedSize;
-  
-  if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
-    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
-    //
-    // Compare the URI to see whether we already have a cache for this file.
-    //
-    if ((Cache->RequestData != NULL) &&
-        (Cache->RequestData->Url != NULL) &&
-        (StrCmp (Uri, Cache->RequestData->Url) == 0)) 
-    {
-      //
-      // Hit cache, check buffer size.
-      //
-      if (*BufferSize < Cache->EntityLength) {
-        *BufferSize = Cache->EntityLength;
-        return EFI_BUFFER_TOO_SMALL;
-      }
-
-      //
-      // Fill data to buffer.
-      //
-      CopyedSize = 0;
-      NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
-        EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
-        if (*BufferSize > CopyedSize) {
-          CopyMem (
-            Buffer + CopyedSize,
-            EntityData->DataStart,
-            MIN (EntityData->DataLength, *BufferSize - CopyedSize)
-            );
-          CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
-        }
-      }
-      *BufferSize = CopyedSize;
-      return EFI_SUCCESS;
-    }
-  }
-
-  return EFI_NOT_FOUND;
-}
-
-/**
-  Release all the resource of a cache item.
-
-  @param[in]          Cache         The pointer to the cache item.
-
-**/
-VOID
-HttpBootFreeCache (
-  IN  HTTP_BOOT_CACHE_CONTENT    *Cache
-  )
-{
-  UINTN                       Index;
-  LIST_ENTRY                  *Entry;
-  LIST_ENTRY                  *NextEntry;
-  HTTP_BOOT_ENTITY_DATA       *EntityData;
-
-  if (Cache != NULL) {
-    //
-    // Free the request data
-    //
-    if (Cache->RequestData != NULL) {
-      if (Cache->RequestData->Url != NULL) {
-        FreePool (Cache->RequestData->Url);
-      }
-      FreePool (Cache->RequestData);
-    }
-
-    //
-    // Free the response header
-    //
-    if (Cache->ResponseData != NULL) {
-      if (Cache->ResponseData->Headers != NULL) {
-        for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
-          FreePool (Cache->ResponseData->Headers[Index].FieldName);
-          FreePool (Cache->ResponseData->Headers[Index].FieldValue);
-        }
-        FreePool (Cache->ResponseData->Headers);
-      }
-    }
-
-    //
-    // Free the response body
-    //
-    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
-      EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
-      if (EntityData->Block != NULL) {
-        FreePool (EntityData->Block);
-      }
-      RemoveEntryList (&EntityData->Link);
-      FreePool (EntityData);
-    }
-
-    FreePool (Cache);
-  }
-}
-
-/**
-  Clean up all cached data.
-
-  @param[in]          Private         The pointer to the driver's private data.
-
-**/
-VOID
-HttpBootFreeCacheList (
-  IN     HTTP_BOOT_PRIVATE_DATA   *Private
-  )
-{
-  LIST_ENTRY                  *Entry;
-  LIST_ENTRY                  *NextEntry;
-  HTTP_BOOT_CACHE_CONTENT     *Cache;
-  
-  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
-    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
-    RemoveEntryList (&Cache->Link);
-    HttpBootFreeCache (Cache);
-  }
-}
-
-/**
-  A callback function to intercept events during message parser.
-
-  This function will be invoked during HttpParseMessageBody() with various events type. An error
-  return status of the callback function will cause the HttpParseMessageBody() aborted.
-
-  @param[in]    EventType          Event type of this callback call.
-  @param[in]    Data               A pointer to data buffer.
-  @param[in]    Length             Length in bytes of the Data.
-  @param[in]    Context            Callback context set by HttpInitMsgParser().
-
-  @retval EFI_SUCCESS              Continue to parser the message body.
-  @retval Others                   Abort the parse.
-**/
-EFI_STATUS
-EFIAPI
-HttpBootGetBootFileCallback (
-  IN HTTP_BODY_PARSE_EVENT      EventType,
-  IN CHAR8                      *Data,
-  IN UINTN                      Length,
-  IN VOID                       *Context
-  )
-{
-  HTTP_BOOT_CALLBACK_DATA      *CallbackData;
-  HTTP_BOOT_ENTITY_DATA        *NewEntityData;
-
-  //
-  // We only care about the entity data.
-  //
-  if (EventType != BodyParseEventOnData) {
-    return EFI_SUCCESS;
-  }
-
-  CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
-
-  //
-  // Save the data into cache list.
-  //
-  NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
-  if (NewEntityData == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-  if (CallbackData->NewBlock) {
-    NewEntityData->Block = CallbackData->Block;
-    CallbackData->Block = NULL;
-  }
-  NewEntityData->DataLength = Length;
-  NewEntityData->DataStart  = (UINT8*) Data;
-  InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
-
-  //
-  // Copy data if caller has provided a buffer.
-  //
-  if (CallbackData->BufferSize > CallbackData->CopyedSize) {
-    CopyMem (
-      CallbackData->Buffer + CallbackData->CopyedSize,
-      Data,
-      MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
-      );
-    CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
-  }
-
-  return EFI_SUCCESS;
-}
-
-/**
-  This function download the boot file by using UEFI HTTP protocol.
-  
-  @param[in]       Private         The pointer to the driver's private data.
-  @param[in]       HeaderOnly      Only request the response header, it could save a lot of time if
-                                   the caller only want to know the size of the requested file.
-  @param[in, out]  BufferSize      On input the size of Buffer in bytes. On output with a return
-                                   code of EFI_SUCCESS, the amount of data transferred to
-                                   Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
-                                   the size of Buffer required to retrieve the requested file.
-  @param[out]      Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
-                                   then the size of the requested file is returned in
-                                   BufferSize.
-
-  @retval EFI_SUCCESS              The file was loaded.
-  @retval EFI_INVALID_PARAMETER    BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
-  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources
-  @retval EFI_BUFFER_TOO_SMALL     The BufferSize is too small to read the current directory entry.
-                                   BufferSize has been updated with the size needed to complete
-                                   the request.
-  @retval Others                   Unexpected error happened.
-
-**/
-EFI_STATUS
-HttpBootGetBootFile (
-  IN     HTTP_BOOT_PRIVATE_DATA   *Private,
-  IN     BOOLEAN                  HeaderOnly,
-  IN OUT UINTN                    *BufferSize,
-     OUT UINT8                    *Buffer
-  )
-{
-  EFI_STATUS                 Status;
-  CHAR8                      *HostName;
-  EFI_HTTP_REQUEST_DATA      *RequestData;
-  HTTP_IO_RESOPNSE_DATA      *ResponseData;
-  HTTP_IO_RESOPNSE_DATA      ResponseBody;
-  HTTP_IO                    *HttpIo;
-  HTTP_IO_HEADER             *HttpIoHeader;
-  VOID                       *Parser;
-  HTTP_BOOT_CALLBACK_DATA    Context;
-  UINTN                      ContentLength;
-  HTTP_BOOT_CACHE_CONTENT    *Cache;
-  UINT8                      *Block;
-  CHAR16                     *Url;
-  
-  ASSERT (Private != NULL);
-  ASSERT (Private->HttpCreated);
-
-  if (BufferSize == NULL) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  if (*BufferSize != 0 && Buffer == NULL) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  //
-  // First, check whether we already cached the requested Uri.
-  //
-  Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));
-  if (Url == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-  AsciiStrToUnicodeStr (Private->BootFileUri, Url);
-  if (!HeaderOnly) {
-    Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);
-    if (Status != EFI_NOT_FOUND) {
-      FreePool (Url);
-      return Status;
-    }
-  }
-
-  //
-  // Not found in cache, try to download it through HTTP.
-  //
-
-  //
-  // 1. Create a temp cache item for the requested URI.
-  //
-  Cache = NULL;
-  if (!HeaderOnly) {
-    Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
-    if (Cache == NULL) {
-      Status = EFI_OUT_OF_RESOURCES;
-      goto ERROR_1;
-    }
-    InitializeListHead (&Cache->EntityDataList);
-  }
-
-  //
-  // 2. Send HTTP request message.
-  //
-
-  //
-  // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
-  //       Host
-  //       Accept
-  //       User-Agent
-  //
-  HttpIoHeader = HttpBootCreateHeader (3);
-  if (HttpIoHeader == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto ERROR_2;
-  }
-
-  //
-  // Add HTTP header field 1: Host
-  //
-  HostName = NULL;
-  Status = HttpUrlGetHostName (
-             Private->BootFileUri,
-             Private->BootFileUriParser,
-             &HostName
-             );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_3;
-  }
-  Status = HttpBootSetHeader (
-             HttpIoHeader,
-             HTTP_FIELD_NAME_HOST,
-             HostName
-             );
-  FreePool (HostName);
-  if (EFI_ERROR (Status)) {
-    goto ERROR_3;
-  }
-
-  //
-  // Add HTTP header field 2: Accept
-  //
-  Status = HttpBootSetHeader (
-             HttpIoHeader,
-             HTTP_FIELD_NAME_ACCEPT,
-             "*/*"
-             );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_3;
-  }
-
-  //
-  // Add HTTP header field 3: User-Agent
-  //
-  Status = HttpBootSetHeader (
-             HttpIoHeader,
-             HTTP_FIELD_NAME_USER_AGENT,
-             HTTP_USER_AGENT_EFI_HTTP_BOOT
-             );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_3;
-  }
-
-  //
-  // 2.2 Build the rest of HTTP request info.
-  //
-  RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
-  if (RequestData == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto ERROR_3;
-  }
-  RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
-  RequestData->Url = Url;
-  if (RequestData->Url == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto ERROR_4;
-  }
-  AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);
-
-  //
-  // 2.3 Record the request info in a temp cache item.
-  //
-  if (!HeaderOnly) {
-    Cache->RequestData = RequestData;
-  }
-
-  //
-  // 2.4 Send out the request to HTTP server.
-  //
-  HttpIo = &Private->HttpIo;
-  Status = HttpIoSendRequest (
-             HttpIo,
-             RequestData,
-             HttpIoHeader->HeaderCount,
-             HttpIoHeader->Headers,
-             0,
-             NULL
-            );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_4;
-  }
-
-  //
-  // 3. Receive HTTP response message.
-  //
-
-  //
-  // 3.1 First step, use zero BodyLength to only receive the response headers.
-  //
-  ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));
-  if (ResponseData == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto ERROR_4;
-  }
-  Status = HttpIoRecvResponse (
-             &Private->HttpIo,
-             TRUE,
-             ResponseData
-             );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_5;
-  }
-
-  //
-  // 3.2 Cache the response header.
-  //
-  if (!HeaderOnly) {
-    Cache->ResponseData = ResponseData;
-  }
-  
-  //
-  // 3.3 Init a message-body parser from the header information.
-  //
-  Parser = NULL;
-  Context.NewBlock   = FALSE;
-  Context.Block      = NULL;
-  Context.CopyedSize = 0;
-  Context.Buffer     = Buffer;
-  Context.BufferSize = *BufferSize;
-  Context.Cache      = Cache;
-  Status = HttpInitMsgParser (
-             HeaderOnly? HttpMethodHead : HttpMethodGet,
-             ResponseData->Response.StatusCode,
-             ResponseData->HeaderCount,
-             ResponseData->Headers,
-             HttpBootGetBootFileCallback,
-             (VOID*) &Context,
-             &Parser
-             );
-  if (EFI_ERROR (Status)) {
-    goto ERROR_6;
-  }
-
-  //
-  // 3.4 Continue to receive and parse message-body if needed.
-  //
-  if (!HeaderOnly) {
-    ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));
-    while (!HttpIsMessageComplete (Parser)) {
-      //
-      // Allocate a new block to hold the message-body.
-      //
-      Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
-      if (Block == NULL) {
-        Status = EFI_OUT_OF_RESOURCES;
-        goto ERROR_6;
-      }
-      ResponseBody.Body       = (CHAR8*) Block;
-      ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
-      Status = HttpIoRecvResponse (
-                 &Private->HttpIo,
-                 FALSE,
-                 &ResponseBody
-                 );
-      if (EFI_ERROR (Status)) {
-        goto ERROR_6;
-      }
-
-      //
-      // Parse the new received block of the message-body, the block will be saved in cache.
-      //
-      Context.NewBlock = TRUE;
-      Context.Block    = Block;
-      Status = HttpParseMessageBody (
-                 Parser,
-                 ResponseBody.BodyLength,
-                 ResponseBody.Body
-                 );
-      if (EFI_ERROR (Status)) {
-        goto ERROR_6;
-      }
-    }
-  }
-  
-  //
-  // 3.5 Message-body receive & parse is completed, get the file size.
-  //
-  Status = HttpGetEntityLength (Parser, &ContentLength);
-  if (EFI_ERROR (Status)) {
-    goto ERROR_6;
-  }
-
-  if (*BufferSize < ContentLength) {
-    Status = EFI_BUFFER_TOO_SMALL;
-  }
-  *BufferSize = ContentLength;
-
-  //
-  // 4. Save the cache item to driver's cache list and return.
-  //
-  if (!HeaderOnly) {
-    Cache->EntityLength = ContentLength;
-    InsertTailList (&Private->CacheList, &Cache->Link);
-  }
-
-  if (Parser != NULL) {
-    HttpFreeMsgParser (Parser);
-  }
-
-  return EFI_SUCCESS;
-  
-ERROR_6:
-  if (Parser != NULL) {
-    HttpFreeMsgParser (Parser);
-  }
-  if (Context.Block != NULL) {
-    FreePool (Context.Block);
-  }
-  HttpBootFreeCache (Cache);
-  
-ERROR_5:
-  if (ResponseData != NULL) {
-    FreePool (ResponseData);
-  }
-ERROR_4:
-  if (RequestData != NULL) {
-    FreePool (RequestData);
-  }
-ERROR_3:
-  HttpBootFreeHeader (HttpIoHeader);
-ERROR_2:
-  if (Cache != NULL) {
-    FreePool (Cache);
-  }
-ERROR_1:
-  if (Url != NULL) {
-    FreePool (Url);
-  }
-
-  return Status;
-}
+/** @file\r
+  Implementation of the boot file download function.\r
+\r
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials are licensed and made available under \r
+the terms and conditions of the BSD License that accompanies this distribution.  \r
+The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php.                                          \r
+    \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "HttpBootDxe.h"\r
+\r
+/**\r
+  Update the IP and URL device path node to include the boot resource information.\r
+\r
+  @param[in]    Private            The pointer to the driver's private data.\r
+\r
+  @retval EFI_SUCCESS              Device patch successfully updated.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
+  @retval Others                   Unexpected error happened.\r
+  \r
+**/\r
+EFI_STATUS\r
+HttpBootUpdateDevicePath (\r
+  IN   HTTP_BOOT_PRIVATE_DATA   *Private\r
+  )\r
+{\r
+  EFI_DEV_PATH               *Node;\r
+  EFI_DEVICE_PATH_PROTOCOL   *TmpDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL   *NewDevicePath;\r
+  UINTN                      Length;\r
+  EFI_STATUS                 Status;\r
+\r
+  TmpDevicePath = NULL;\r
+  \r
+  //\r
+  // Update the IP node with DHCP assigned information.\r
+  //\r
+  if (!Private->UsingIpv6) {\r
+    Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));\r
+    if (Node == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;\r
+    Node->Ipv4.Header.SubType = MSG_IPv4_DP;\r
+    SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));\r
+    CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+    Node->Ipv4.RemotePort      = Private->Port;\r
+    Node->Ipv4.Protocol        = EFI_IP_PROTO_TCP;\r
+    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
+  } else {\r
+    Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
+    if (Node == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\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
+  // Update the URI node with the boot file URI.\r
+  //\r
+  Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);\r
+  Node = AllocatePool (Length);\r
+  if (Node == NULL) {\r
+    FreePool (TmpDevicePath);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;\r
+  Node->DevPath.SubType = MSG_URI_DP;\r
+  SetDevicePathNodeLength (Node, Length);\r
+  CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));\r
+  \r
+  NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+  FreePool (Node);\r
+  FreePool (TmpDevicePath);\r
+  if (NewDevicePath == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\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
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Parse the boot file URI information from the selected Dhcp4 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
+HttpBootDhcp4ExtractUriInfo (\r
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private\r
+  )\r
+{\r
+  HTTP_BOOT_DHCP4_PACKET_CACHE    *SelectOffer;\r
+  HTTP_BOOT_DHCP4_PACKET_CACHE    *HttpOffer;\r
+  UINT32                          SelectIndex;\r
+  UINT32                          ProxyIndex;\r
+  EFI_DHCP4_PACKET_OPTION         *Option;\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
+\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].Dhcp4;\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].Dhcp4;\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_DHCP4_TAG_INDEX_DNS_SERVER];\r
+    ASSERT (Option != NULL);\r
+    Status = HttpBootRegisterIp4Dns (\r
+               Private,\r
+               Option->Length,\r
+               Option->Data\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\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_DHCP4_TAG_INDEX_BOOTFILE]->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_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
+\r
+  \r
+  //\r
+  // All boot informations are valid here.\r
+  //\r
+  AsciiPrint ("\n  URI: %a", Private->BootFileUri);\r
+\r
+  //\r
+  // Update the device path to include the IP and boot URI information.\r
+  //\r
+  Status = HttpBootUpdateDevicePath (Private);\r
+\r
+  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
+  @param[in, out]    Private        The pointer to the driver's private data.\r
+\r
+  @retval EFI_SUCCESS          Successfully obtained all the boot information .\r
+  @retval Others               Failed to retrieve the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDiscoverBootInfo (\r
+  IN OUT HTTP_BOOT_PRIVATE_DATA   *Private\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  \r
+  //\r
+  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
+  // other Http boot information.\r
+  //\r
+  Status = HttpBootDhcp (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (!Private->UsingIpv6) {\r
+    Status = HttpBootDhcp4ExtractUriInfo (Private);\r
+  } else {\r
+    Status = HttpBootDhcp6ExtractUriInfo (Private);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Create a HttpIo instance for the file download.\r
+\r
+  @param[in]    Private        The pointer to the driver's private data.\r
+\r
+  @retval EFI_SUCCESS          Successfully created.\r
+  @retval Others               Failed to create HttpIo.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootCreateHttpIo (\r
+  IN     HTTP_BOOT_PRIVATE_DATA       *Private\r
+  )\r
+{\r
+  HTTP_IO_CONFIG_DATA          ConfigData;\r
+  EFI_STATUS                   Status;\r
+\r
+  ASSERT (Private != NULL);\r
+\r
+  ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
+  if (!Private->UsingIpv6) {\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
+    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
+             Private->Image,\r
+             Private->Controller,\r
+             Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,\r
+             &ConfigData,\r
+             &Private->HttpIo\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private->HttpCreated = TRUE;\r
+  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
+  @param[in]          Cache         The pointer to the cache item.\r
+\r
+**/\r
+VOID\r
+HttpBootFreeCache (\r
+  IN  HTTP_BOOT_CACHE_CONTENT    *Cache\r
+  )\r
+{\r
+  UINTN                       Index;\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *NextEntry;\r
+  HTTP_BOOT_ENTITY_DATA       *EntityData;\r
+\r
+  if (Cache != NULL) {\r
+    //\r
+    // Free the request data\r
+    //\r
+    if (Cache->RequestData != NULL) {\r
+      if (Cache->RequestData->Url != NULL) {\r
+        FreePool (Cache->RequestData->Url);\r
+      }\r
+      FreePool (Cache->RequestData);\r
+    }\r
+\r
+    //\r
+    // Free the response header\r
+    //\r
+    if (Cache->ResponseData != NULL) {\r
+      if (Cache->ResponseData->Headers != NULL) {\r
+        for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {\r
+          FreePool (Cache->ResponseData->Headers[Index].FieldName);\r
+          FreePool (Cache->ResponseData->Headers[Index].FieldValue);\r
+        }\r
+        FreePool (Cache->ResponseData->Headers);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Free the response body\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {\r
+      EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);\r
+      if (EntityData->Block != NULL) {\r
+        FreePool (EntityData->Block);\r
+      }\r
+      RemoveEntryList (&EntityData->Link);\r
+      FreePool (EntityData);\r
+    }\r
+\r
+    FreePool (Cache);\r
+  }\r
+}\r
+\r
+/**\r
+  Clean up all cached data.\r
+\r
+  @param[in]          Private         The pointer to the driver's private data.\r
+\r
+**/\r
+VOID\r
+HttpBootFreeCacheList (\r
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private\r
+  )\r
+{\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *NextEntry;\r
+  HTTP_BOOT_CACHE_CONTENT     *Cache;\r
+  \r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {\r
+    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
+    RemoveEntryList (&Cache->Link);\r
+    HttpBootFreeCache (Cache);\r
+  }\r
+}\r
+\r
+/**\r
+  A callback function to intercept events during message parser.\r
+\r
+  This function will be invoked during HttpParseMessageBody() with various events type. An error\r
+  return status of the callback function will cause the HttpParseMessageBody() aborted.\r
+\r
+  @param[in]    EventType          Event type of this callback call.\r
+  @param[in]    Data               A pointer to data buffer.\r
+  @param[in]    Length             Length in bytes of the Data.\r
+  @param[in]    Context            Callback context set by HttpInitMsgParser().\r
+\r
+  @retval EFI_SUCCESS              Continue to parser the message body.\r
+  @retval Others                   Abort the parse.\r
\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootGetBootFileCallback (\r
+  IN HTTP_BODY_PARSE_EVENT      EventType,\r
+  IN CHAR8                      *Data,\r
+  IN UINTN                      Length,\r
+  IN VOID                       *Context\r
+  )\r
+{\r
+  HTTP_BOOT_CALLBACK_DATA      *CallbackData;\r
+  HTTP_BOOT_ENTITY_DATA        *NewEntityData;\r
+\r
+  //\r
+  // We only care about the entity data.\r
+  //\r
+  if (EventType != BodyParseEventOnData) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;\r
+  //\r
+  // Copy data if caller has provided a buffer.\r
+  //\r
+  if (CallbackData->BufferSize > CallbackData->CopyedSize) {\r
+    CopyMem (\r
+      CallbackData->Buffer + CallbackData->CopyedSize,\r
+      Data,\r
+      MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)\r
+      );\r
+    CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);\r
+  }\r
+\r
+  //\r
+  // The caller doesn't provide a buffer, save the block into cache list.\r
+  //\r
+  if (CallbackData->Cache != NULL) {\r
+    NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));\r
+    if (NewEntityData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    if (CallbackData->NewBlock) {\r
+      NewEntityData->Block = CallbackData->Block;\r
+      CallbackData->Block = NULL;\r
+    }\r
+    NewEntityData->DataLength = Length;\r
+    NewEntityData->DataStart  = (UINT8*) Data;\r
+    InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function download the boot file by using UEFI HTTP protocol.\r
+  \r
+  @param[in]       Private         The pointer to the driver's private data.\r
+  @param[in]       HeaderOnly      Only request the response header, it could save a lot of time if\r
+                                   the caller only want to know the size of the requested file.\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              The file was loaded.\r
+  @retval EFI_INVALID_PARAMETER    BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources\r
+  @retval EFI_BUFFER_TOO_SMALL     The BufferSize is too small to read the current directory entry.\r
+                                   BufferSize has been updated with the size needed to complete\r
+                                   the request.\r
+  @retval Others                   Unexpected error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootGetBootFile (\r
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private,\r
+  IN     BOOLEAN                  HeaderOnly,\r
+  IN OUT UINTN                    *BufferSize,\r
+     OUT UINT8                    *Buffer\r
+  )\r
+{\r
+  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                    *HttpIo;\r
+  HTTP_IO_HEADER             *HttpIoHeader;\r
+  VOID                       *Parser;\r
+  HTTP_BOOT_CALLBACK_DATA    Context;\r
+  UINTN                      ContentLength;\r
+  HTTP_BOOT_CACHE_CONTENT    *Cache;\r
+  UINT8                      *Block;\r
+  CHAR16                     *Url;\r
+  \r
+  ASSERT (Private != NULL);\r
+  ASSERT (Private->HttpCreated);\r
+\r
+  if (BufferSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*BufferSize != 0 && Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // First, check whether we already cached the requested Uri.\r
+  //\r
+  Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));\r
+  if (Url == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  AsciiStrToUnicodeStr (Private->BootFileUri, Url);\r
+  if (!HeaderOnly) {\r
+    Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);\r
+    if (Status != EFI_NOT_FOUND) {\r
+      FreePool (Url);\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Not found in cache, try to download it through HTTP.\r
+  //\r
+\r
+  //\r
+  // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.\r
+  //\r
+  Cache = NULL;\r
+  if ((!HeaderOnly) && (*BufferSize == 0)) {\r
+    Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));\r
+    if (Cache == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ERROR_1;\r
+    }\r
+    InitializeListHead (&Cache->EntityDataList);\r
+  }\r
+\r
+  //\r
+  // 2. Send HTTP request message.\r
+  //\r
+\r
+  //\r
+  // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:\r
+  //       Host\r
+  //       Accept\r
+  //       User-Agent\r
+  //\r
+  HttpIoHeader = HttpBootCreateHeader (3);\r
+  if (HttpIoHeader == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ERROR_2;\r
+  }\r
+\r
+  //\r
+  // Add HTTP header field 1: Host\r
+  //\r
+  HostName = NULL;\r
+  Status = HttpUrlGetHostName (\r
+             Private->BootFileUri,\r
+             Private->BootFileUriParser,\r
+             &HostName\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_3;\r
+  }\r
+  Status = HttpBootSetHeader (\r
+             HttpIoHeader,\r
+             HTTP_FIELD_NAME_HOST,\r
+             HostName\r
+             );\r
+  FreePool (HostName);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_3;\r
+  }\r
+\r
+  //\r
+  // Add HTTP header field 2: Accept\r
+  //\r
+  Status = HttpBootSetHeader (\r
+             HttpIoHeader,\r
+             HTTP_FIELD_NAME_ACCEPT,\r
+             "*/*"\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_3;\r
+  }\r
+\r
+  //\r
+  // Add HTTP header field 3: User-Agent\r
+  //\r
+  Status = HttpBootSetHeader (\r
+             HttpIoHeader,\r
+             HTTP_FIELD_NAME_USER_AGENT,\r
+             HTTP_USER_AGENT_EFI_HTTP_BOOT\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_3;\r
+  }\r
+\r
+  //\r
+  // 2.2 Build the rest of HTTP request info.\r
+  //\r
+  RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));\r
+  if (RequestData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ERROR_3;\r
+  }\r
+  RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;\r
+  RequestData->Url = Url;\r
+  if (RequestData->Url == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ERROR_4;\r
+  }\r
+  AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);\r
+\r
+  //\r
+  // 2.3 Record the request info in a temp cache item.\r
+  //\r
+  if (Cache != NULL) {\r
+    Cache->RequestData = RequestData;\r
+  }\r
+\r
+  //\r
+  // 2.4 Send out the request to HTTP server.\r
+  //\r
+  HttpIo = &Private->HttpIo;\r
+  Status = HttpIoSendRequest (\r
+             HttpIo,\r
+             RequestData,\r
+             HttpIoHeader->HeaderCount,\r
+             HttpIoHeader->Headers,\r
+             0,\r
+             NULL\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_4;\r
+  }\r
+\r
+  //\r
+  // 3. Receive HTTP response message.\r
+  //\r
+\r
+  //\r
+  // 3.1 First step, use zero BodyLength to only receive the response headers.\r
+  //\r
+  ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));\r
+  if (ResponseData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ERROR_4;\r
+  }\r
+  Status = HttpIoRecvResponse (\r
+             &Private->HttpIo,\r
+             TRUE,\r
+             ResponseData\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_5;\r
+  }\r
+\r
+  //\r
+  // 3.2 Cache the response header.\r
+  //\r
+  if (Cache != NULL) {\r
+    Cache->ResponseData = ResponseData;\r
+  }\r
+  \r
+  //\r
+  // 3.3 Init a message-body parser from the header information.\r
+  //\r
+  Parser = NULL;\r
+  Context.NewBlock   = FALSE;\r
+  Context.Block      = NULL;\r
+  Context.CopyedSize = 0;\r
+  Context.Buffer     = Buffer;\r
+  Context.BufferSize = *BufferSize;\r
+  Context.Cache      = Cache;\r
+  Status = HttpInitMsgParser (\r
+             HeaderOnly? HttpMethodHead : HttpMethodGet,\r
+             ResponseData->Response.StatusCode,\r
+             ResponseData->HeaderCount,\r
+             ResponseData->Headers,\r
+             HttpBootGetBootFileCallback,\r
+             (VOID*) &Context,\r
+             &Parser\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_6;\r
+  }\r
+\r
+  //\r
+  // 3.4 Continue to receive and parse message-body if needed.\r
+  //\r
+  Block = NULL;\r
+  if (!HeaderOnly) {\r
+    ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));\r
+    while (!HttpIsMessageComplete (Parser)) {\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
+      //\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
+  // 3.5 Message-body receive & parse is completed, get the file size.\r
+  //\r
+  Status = HttpGetEntityLength (Parser, &ContentLength);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR_6;\r
+  }\r
+\r
+  if (*BufferSize < ContentLength) {\r
+    Status = EFI_BUFFER_TOO_SMALL;\r
+  }\r
+  *BufferSize = ContentLength;\r
+\r
+  //\r
+  // 4. Save the cache item to driver's cache list and return.\r
+  //\r
+  if (Cache != NULL) {\r
+    Cache->EntityLength = ContentLength;\r
+    InsertTailList (&Private->CacheList, &Cache->Link);\r
+  }\r
+\r
+  if (Parser != NULL) {\r
+    HttpFreeMsgParser (Parser);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+  \r
+ERROR_6:\r
+  if (Parser != NULL) {\r
+    HttpFreeMsgParser (Parser);\r
+  }\r
+  if (Context.Block != NULL) {\r
+    FreePool (Context.Block);\r
+  }\r
+  HttpBootFreeCache (Cache);\r
+  \r
+ERROR_5:\r
+  if (ResponseData != NULL) {\r
+    FreePool (ResponseData);\r
+  }\r
+ERROR_4:\r
+  if (RequestData != NULL) {\r
+    FreePool (RequestData);\r
+  }\r
+ERROR_3:\r
+  HttpBootFreeHeader (HttpIoHeader);\r
+ERROR_2:\r
+  if (Cache != NULL) {\r
+    FreePool (Cache);\r
+  }\r
+ERROR_1:\r
+  if (Url != NULL) {\r
+    FreePool (Url);\r
+  }\r
+\r
+  return Status;\r
+}\r