]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpDxe/HttpImpl.c
NetworkPkg/HttpDxe: HTTPS support over IPv4 and IPv6
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpImpl.c
index 2b62dc5db299f1ce1fe48b9ed43be5ccf67a31d8..77aa64a2b99668ef6b65920e6820d3c665615332 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.\r
 \r
-  Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
@@ -38,8 +39,11 @@ EFI_HTTP_PROTOCOL  mEfiHttpTemplate = {
   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:\r
                                   This is NULL.\r
                                   HttpConfigData is NULL.\r
-                                  HttpConfigData->AccessPoint is NULL.\r
-  @retval EFI_NOT_STARTED         The HTTP instance is not configured.\r
+                                  HttpInstance->LocalAddressIsIPv6 is FALSE and\r
+                                  HttpConfigData->IPv4Node is NULL.\r
+                                  HttpInstance->LocalAddressIsIPv6 is TRUE and\r
+                                  HttpConfigData->IPv6Node is NULL.\r
+  @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -51,30 +55,42 @@ EfiHttpGetModeData (
 {\r
   HTTP_PROTOCOL                 *HttpInstance;\r
 \r
+  //\r
+  // Check input parameters.\r
+  //\r
   if ((This == NULL) || (HttpConfigData == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
-  \r
+\r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
   ASSERT (HttpInstance != NULL);\r
 \r
-  if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
-    return EFI_NOT_STARTED;\r
+  if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
+      (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (HttpConfigData->AccessPoint.IPv4Node == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
+  if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
+    return EFI_NOT_STARTED;\r
   }\r
 \r
   HttpConfigData->HttpVersion        = HttpInstance->HttpVersion;\r
   HttpConfigData->TimeOutMillisec    = HttpInstance->TimeOutMillisec;\r
   HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;\r
 \r
-  CopyMem (\r
-    HttpConfigData->AccessPoint.IPv4Node,\r
-    &HttpInstance->IPv4Node,\r
-    sizeof (HttpInstance->IPv4Node)\r
+  if (HttpInstance->LocalAddressIsIPv6) {\r
+    CopyMem (\r
+      HttpConfigData->AccessPoint.IPv6Node,\r
+      &HttpInstance->Ipv6Node,\r
+      sizeof (HttpInstance->Ipv6Node)\r
     );\r
+  } else {\r
+    CopyMem (\r
+      HttpConfigData->AccessPoint.IPv4Node,\r
+      &HttpInstance->IPv4Node,\r
+      sizeof (HttpInstance->IPv4Node)\r
+      );\r
+  }\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -89,8 +105,8 @@ EfiHttpGetModeData (
   connections with remote hosts, canceling all asynchronous tokens, and flush request\r
   and response buffers without informing the appropriate hosts.\r
 \r
-  Except for GetModeData() and Configure(), No other EFI HTTP function can be executed\r
-  by this instance until the Configure() function is executed and returns successfully.\r
+  No other EFI HTTP function can be executed by this instance until the Configure()\r
+  function is executed and returns successfully.\r
 \r
   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.\r
   @param[in]  HttpConfigData      Pointer to the configure data to configure the instance.\r
@@ -119,8 +135,14 @@ EfiHttpConfigure (
 {\r
   HTTP_PROTOCOL                 *HttpInstance;\r
   EFI_STATUS                    Status;\r
-\r
-  if (This == NULL) {\r
+  \r
+  //\r
+  // Check input parameters.\r
+  //\r
+  if (This == NULL ||\r
+      (HttpConfigData != NULL && \r
+       ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
+        (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -128,18 +150,7 @@ EfiHttpConfigure (
   ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);\r
 \r
   if (HttpConfigData != NULL) {\r
-    //\r
-    // Check input parameters.\r
-    //\r
-    if (HttpConfigData->LocalAddressIsIPv6) {\r
-      if (HttpConfigData->AccessPoint.IPv6Node == NULL) {\r
-        return EFI_INVALID_PARAMETER;\r
-      }\r
-    } else {\r
-      if (HttpConfigData->AccessPoint.IPv4Node == NULL) {\r
-        return EFI_INVALID_PARAMETER;\r
-      }\r
-    }\r
+\r
     //\r
     // Now configure this HTTP instance.\r
     //\r
@@ -150,33 +161,39 @@ EfiHttpConfigure (
     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;\r
     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;\r
     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;\r
-\r
-    if (HttpConfigData->LocalAddressIsIPv6) {\r
-      return EFI_UNSUPPORTED;\r
+    \r
+    if (HttpConfigData->LocalAddressIsIPv6) { \r
+      CopyMem (\r
+        &HttpInstance->Ipv6Node,\r
+        HttpConfigData->AccessPoint.IPv6Node,\r
+        sizeof (HttpInstance->Ipv6Node)\r
+        );\r
     } else {\r
       CopyMem (\r
         &HttpInstance->IPv4Node,\r
         HttpConfigData->AccessPoint.IPv4Node,\r
         sizeof (HttpInstance->IPv4Node)\r
         );\r
-\r
-      HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;\r
-      return EFI_SUCCESS;\r
     }\r
+    \r
+    //\r
+    // Creat Tcp child\r
+    //\r
+    Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    \r
+    HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;\r
+    return EFI_SUCCESS;\r
 \r
   } else {\r
-    if (HttpInstance->LocalAddressIsIPv6) {\r
-      return EFI_UNSUPPORTED;\r
-    } else {\r
-      HttpCleanProtocol (HttpInstance);\r
-      Status = HttpInitProtocol (HttpInstance->Service, HttpInstance);\r
-      if (EFI_ERROR (Status)) {\r
-        return Status;\r
-      }\r
-\r
-      HttpInstance->State = HTTP_STATE_UNCONFIGED;\r
-      return EFI_SUCCESS;\r
-    }\r
+    //\r
+    // Reset all the resources related to HttpInsance.\r
+    //\r
+    HttpCleanProtocol (HttpInstance);\r
+    HttpInstance->State = HTTP_STATE_UNCONFIGED;\r
+    return EFI_SUCCESS;\r
   }\r
 }\r
  \r
@@ -200,6 +217,7 @@ EfiHttpConfigure (
                                   implementation.\r
   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:\r
                                   This is NULL.\r
+                                  Token is NULL.\r
                                   Token->Message is NULL.\r
                                   Token->Message->Body is not NULL,\r
                                   Token->Message->BodyLength is non-zero, and\r
@@ -218,179 +236,264 @@ EfiHttpRequest (
   VOID                          *UrlParser;\r
   EFI_STATUS                    Status;\r
   CHAR8                         *HostName;\r
+  UINTN                         HostNameSize;\r
   UINT16                        RemotePort;\r
   HTTP_PROTOCOL                 *HttpInstance;\r
   BOOLEAN                       Configure;\r
   BOOLEAN                       ReConfigure;\r
-  CHAR8                         *RequestStr;\r
+  BOOLEAN                       TlsConfigure;\r
+  CHAR8                         *RequestMsg;\r
   CHAR8                         *Url;\r
   UINTN                         UrlLen;\r
   CHAR16                        *HostNameStr;\r
   HTTP_TOKEN_WRAP               *Wrap;\r
-  HTTP_TCP_TOKEN_WRAP           *TcpWrap;\r
   CHAR8                         *FileUrl;\r
-  \r
+  UINTN                         RequestMsgSize;\r
+\r
+  //\r
+  // Initializations\r
+  //\r
+  Url = NULL;\r
+  UrlParser = NULL;\r
+  RemotePort = 0;\r
+  HostName = NULL;\r
+  RequestMsg = NULL;\r
+  HostNameStr = NULL;\r
+  Wrap = NULL;\r
+  FileUrl = NULL;\r
+  TlsConfigure = FALSE;\r
+\r
   if ((This == NULL) || (Token == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   HttpMsg = Token->Message;\r
-  if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) {\r
+  if (HttpMsg == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  //\r
-  // Current implementation does not support POST/PUT method.\r
-  // If future version supports these two methods, Request could be NULL for a special case that to send large amounts\r
-  // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.\r
-  // \r
-  //\r
   Request = HttpMsg->Data.Request;\r
-  if ((Request == NULL) || (Request->Url == NULL)) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
 \r
   //\r
-  // Only support GET and HEAD method in current implementation.\r
+  // Only support GET, HEAD, PUT and POST method in current implementation.\r
   //\r
-  if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) {\r
+  if ((Request != NULL) && (Request->Method != HttpMethodGet) &&\r
+      (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost)) {\r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
   ASSERT (HttpInstance != NULL);\r
 \r
+  //\r
+  // Capture the method into HttpInstance.\r
+  //\r
+  if (Request != NULL) {\r
+    HttpInstance->Method = Request->Method;\r
+  }\r
+\r
   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
     return EFI_NOT_STARTED;\r
   }\r
 \r
-  if (HttpInstance->LocalAddressIsIPv6) {\r
-    return EFI_UNSUPPORTED;\r
-  }\r
+  if (Request == NULL) {\r
+    //\r
+    // Request would be NULL only for PUT/POST operation (in the current implementation)\r
+    //\r
+    if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  //\r
-  // Check whether the token already existed.\r
-  //\r
-  if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {\r
-    return EFI_ACCESS_DENIED;   \r
-  }  \r
+    //\r
+    // For PUT/POST, we need to have the TCP already configured. Bail out if it is not!\r
+    //\r
+    if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  Url         = NULL;\r
-  HostName    = NULL;\r
-  Wrap        = NULL;\r
-  HostNameStr = NULL;\r
-  TcpWrap     = NULL;\r
+    //\r
+    // We need to have the Message Body for sending the HTTP message across in these cases.\r
+    //\r
+    if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  //\r
-  // Parse the URI of the remote host.\r
-  //\r
-  UrlLen = StrLen (Request->Url) + 1;\r
-  if (UrlLen > HTTP_URL_BUFFER_LEN) {\r
-    Url = AllocateZeroPool (UrlLen);\r
-    if (Url == NULL) {\r
-      return EFI_OUT_OF_RESOURCES;\r
+    //\r
+    // Use existing TCP instance to transmit the packet.\r
+    //\r
+    Configure   = FALSE;\r
+    ReConfigure = FALSE;\r
+  } else {\r
+    //\r
+    // Check whether the token already existed.\r
+    //\r
+    if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {\r
+      return EFI_ACCESS_DENIED;\r
     }\r
-    FreePool (HttpInstance->Url);\r
-    HttpInstance->Url = Url;    \r
-  }  \r
 \r
-  UnicodeStrToAsciiStr (Request->Url, Url);\r
-  UrlParser = NULL;\r
-  Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error1;\r
-  }\r
+    //\r
+    // Parse the URI of the remote host.\r
+    //\r
+    Url = HttpInstance->Url;\r
+    UrlLen = StrLen (Request->Url) + 1;\r
+    if (UrlLen > HTTP_URL_BUFFER_LEN) {\r
+      Url = AllocateZeroPool (UrlLen);\r
+      if (Url == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      FreePool (HttpInstance->Url);\r
+      HttpInstance->Url = Url;\r
+    }\r
 \r
-  RequestStr = NULL;\r
-  HostName   = NULL;\r
-  Status     = HttpUrlGetHostName (Url, UrlParser, &HostName);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error1;\r
-  }\r
 \r
-  Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
-  if (EFI_ERROR (Status)) {\r
-    RemotePort = HTTP_DEFAULT_PORT;\r
-  }\r
+    UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);\r
 \r
-  Configure   = TRUE;\r
-  ReConfigure = TRUE;  \r
+    //\r
+    // From the information in Url, the HTTP instance will \r
+    // be able to determine whether to use http or https.\r
+    //\r
+    HttpInstance->UseHttps = IsHttpsUrl (Url);\r
 \r
-  if (HttpInstance->RemoteHost == NULL && HttpInstance->RemotePort == 0) {\r
     //\r
-    // Request() is called the first time. \r
+    // Check whether we need to create Tls child and open the TLS protocol.\r
     //\r
-    ReConfigure = FALSE;\r
-  } else {\r
-    if ((HttpInstance->RemotePort == RemotePort) &&\r
-        (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
+    if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {\r
       //\r
-      // Host Name and port number of the request URL are the same with previous call to Request().\r
-      // Check whether previous TCP packet sent out.\r
+      // Use TlsSb to create Tls child and open the TLS protocol.\r
       //\r
-      if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {\r
-        //\r
-        // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
-        //\r
-        Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
-        if (Wrap == NULL) {\r
-          Status = EFI_OUT_OF_RESOURCES;\r
-          goto Error1;\r
-        }\r
+      HttpInstance->TlsChildHandle = TlsCreateChild (\r
+                                       HttpInstance->Service->ImageHandle,\r
+                                       &(HttpInstance->Tls),\r
+                                       &(HttpInstance->TlsConfiguration)\r
+                                       );\r
+      if (HttpInstance->TlsChildHandle == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
 \r
-        Wrap->HttpToken    = Token;\r
-        Wrap->HttpInstance = HttpInstance;\r
+      TlsConfigure = TRUE;\r
+    }\r
 \r
-        Status = HttpCreateTcp4TxEvent (Wrap);\r
-        if (EFI_ERROR (Status)) {\r
-          goto Error1;\r
-        }\r
+    UrlParser = NULL;\r
+    Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error1;\r
+    }\r
 \r
-        Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
-        if (EFI_ERROR (Status)) {\r
-          goto Error1;\r
-        }\r
+    HostName   = NULL;\r
+    Status     = HttpUrlGetHostName (Url, UrlParser, &HostName);\r
+    if (EFI_ERROR (Status)) {\r
+     goto Error1;\r
+    }\r
 \r
-        Wrap->TcpWrap.Method = Request->Method;\r
+    Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
+    if (EFI_ERROR (Status)) {\r
+      if (HttpInstance->UseHttps) {\r
+        RemotePort = HTTPS_DEFAULT_PORT;\r
+      } else {\r
+        RemotePort = HTTP_DEFAULT_PORT;\r
+      }\r
+    }\r
+    //\r
+    // If Configure is TRUE, it indicates the first time to call Request();\r
+    // If ReConfigure is TRUE, it indicates the request URL is not same\r
+    // with the previous call to Request();\r
+    //\r
+    Configure   = TRUE;\r
+    ReConfigure = TRUE;\r
 \r
-        FreePool (HostName);\r
-        \r
+    if (HttpInstance->RemoteHost == NULL) {\r
+      //\r
+      // Request() is called the first time.\r
+      //\r
+      ReConfigure = FALSE;\r
+    } else {\r
+      if ((HttpInstance->RemotePort == RemotePort) &&\r
+          (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && \r
+          (!HttpInstance->UseHttps || (HttpInstance->UseHttps && \r
+                                       !TlsConfigure && \r
+                                       HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {\r
         //\r
-        // Queue the HTTP token and return.\r
+        // Host Name and port number of the request URL are the same with previous call to Request().\r
+        // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.\r
+        // Check whether previous TCP packet sent out.\r
         //\r
-        return EFI_SUCCESS;\r
+\r
+        if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {\r
+          //\r
+          // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
+          //\r
+          Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
+          if (Wrap == NULL) {\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            goto Error1;\r
+          }\r
+\r
+          Wrap->HttpToken    = Token;\r
+          Wrap->HttpInstance = HttpInstance;\r
+\r
+          Status = HttpCreateTcpTxEvent (Wrap);\r
+          if (EFI_ERROR (Status)) {\r
+            goto Error1;\r
+          }\r
+\r
+          Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
+          if (EFI_ERROR (Status)) {\r
+            goto Error1;\r
+          }\r
+\r
+          Wrap->TcpWrap.Method = Request->Method;\r
+\r
+          FreePool (HostName);\r
+\r
+          //\r
+          // Queue the HTTP token and return.\r
+          //\r
+          return EFI_SUCCESS;\r
+        } else {\r
+          //\r
+          // Use existing TCP instance to transmit the packet.\r
+          //\r
+          Configure   = FALSE;\r
+          ReConfigure = FALSE;\r
+        }\r
       } else {\r
         //\r
-        // Use existing TCP instance to transmit the packet.\r
+        // Need close existing TCP instance and create a new TCP instance for data transmit.\r
         //\r
-        Configure   = FALSE;\r
-        ReConfigure = FALSE;\r
-      }\r
-    } else {\r
-      //\r
-      // Need close existing TCP instance and create a new TCP instance for data transmit.\r
-      //\r
-      if (HttpInstance->RemoteHost != NULL) {\r
-        FreePool (HttpInstance->RemoteHost);\r
-        HttpInstance->RemoteHost = NULL;\r
+        if (HttpInstance->RemoteHost != NULL) {\r
+          FreePool (HttpInstance->RemoteHost);\r
+          HttpInstance->RemoteHost = NULL;\r
+          HttpInstance->RemotePort = 0;\r
+        }\r
       }\r
     }\r
   } \r
 \r
   if (Configure) {\r
     //\r
-    // Parse Url for IPv4 address, if failed, perform DNS resolution.\r
+    // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.\r
     //\r
-    Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);\r
+    if (!HttpInstance->LocalAddressIsIPv6) {\r
+      Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);\r
+    } else {\r
+      Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);\r
+    }\r
+\r
     if (EFI_ERROR (Status)) {\r
-      HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (UINT16));\r
+      HostNameSize = AsciiStrSize (HostName);\r
+      HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));\r
       if (HostNameStr == NULL) {\r
         Status = EFI_OUT_OF_RESOURCES;\r
         goto Error1;\r
       }\r
-\r
-      AsciiStrToUnicodeStr (HostName, HostNameStr);\r
-      Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);\r
+      \r
+      AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);\r
+      if (!HttpInstance->LocalAddressIsIPv6) {\r
+        Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);\r
+      } else {\r
+        Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);\r
+      }\r
+      \r
       FreePool (HostNameStr);\r
       if (EFI_ERROR (Status)) {\r
         goto Error1;\r
@@ -410,7 +513,21 @@ EfiHttpRequest (
     //\r
     // The request URL is different from previous calls to Request(), close existing TCP instance.\r
     //\r
-    ASSERT (HttpInstance->Tcp4 != NULL);\r
+    if (!HttpInstance->LocalAddressIsIPv6) {\r
+      ASSERT (HttpInstance->Tcp4 != NULL);\r
+    } else {\r
+      ASSERT (HttpInstance->Tcp6 != NULL);\r
+    }\r
+\r
+    if (HttpInstance->UseHttps && !TlsConfigure) {\r
+      Status = TlsCloseSession (HttpInstance);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error1;\r
+      }\r
+      \r
+      TlsCloseTxRxEvent (HttpInstance);\r
+    }\r
+    \r
     HttpCloseConnection (HttpInstance);\r
     EfiHttpCancel (This, NULL);\r
   }\r
@@ -426,38 +543,35 @@ EfiHttpRequest (
 \r
   Wrap->HttpToken      = Token;\r
   Wrap->HttpInstance   = HttpInstance;\r
-  Wrap->TcpWrap.Method = Request->Method;\r
+  if (Request != NULL) {\r
+    Wrap->TcpWrap.Method = Request->Method;\r
+  }\r
+  \r
+  Status = HttpInitSession (\r
+             HttpInstance, \r
+             Wrap, \r
+             Configure || ReConfigure, \r
+             TlsConfigure\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error2;\r
+  }\r
 \r
-  if (Configure) {\r
-    //\r
-    // Configure TCP instance.\r
-    //\r
-    Status = HttpConfigureTcp4 (HttpInstance, Wrap);\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error1;\r
-    }\r
-    //\r
-    // Connect TCP.\r
-    //\r
-    Status = HttpConnectTcp4 (HttpInstance);\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error2;\r
-    }\r
-  } else {\r
+  if (!Configure && !ReConfigure && !TlsConfigure) {\r
     //\r
     // For the new HTTP token, create TX TCP token events.    \r
     //\r
-    Status = HttpCreateTcp4TxEvent (Wrap);\r
+    Status = HttpCreateTcpTxEvent (Wrap);\r
     if (EFI_ERROR (Status)) {\r
       goto Error1;\r
     }\r
   }\r
-\r
+  \r
   //\r
   // Create request message.\r
   //\r
   FileUrl = Url;\r
-  if (*FileUrl != '/') {\r
+  if (Url != NULL && *FileUrl != '/') {\r
     //\r
     // Convert the absolute-URI to the absolute-path\r
     //\r
@@ -474,55 +588,78 @@ EfiHttpRequest (
       goto Error3;\r
     }\r
   }\r
-  RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, FileUrl);\r
-  if (RequestStr == NULL) {\r
-    Status = EFI_OUT_OF_RESOURCES;\r
-    goto Error3;\r
-  }\r
 \r
-  Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
+  Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);\r
+\r
   if (EFI_ERROR (Status)) {\r
-    goto Error4;\r
+    goto Error3;\r
   }\r
 \r
-  if (HostName != NULL) {\r
-    FreePool (HostName);\r
+  //\r
+  // Every request we insert a TxToken and a response call would remove the TxToken.\r
+  // In cases of PUT/POST, after an initial request-response pair, we would do a\r
+  // continuous request without a response call. So, in such cases, where Request\r
+  // structure is NULL, we would not insert a TxToken.\r
+  //\r
+  if (Request != NULL) {\r
+    Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error4;\r
+    }\r
   }\r
 \r
   //\r
   // Transmit the request message.\r
   //\r
-  Status = HttpTransmitTcp4 (\r
+  Status = HttpTransmitTcp (\r
              HttpInstance,\r
              Wrap,\r
-             (UINT8*) RequestStr,\r
-             AsciiStrLen (RequestStr)\r
+             (UINT8*) RequestMsg,\r
+             RequestMsgSize\r
              );\r
   if (EFI_ERROR (Status)) {\r
     goto Error5;    \r
   }\r
 \r
   DispatchDpc ();\r
-\r
+  \r
+  if (HostName != NULL) {\r
+    FreePool (HostName);\r
+  }\r
+  \r
   return EFI_SUCCESS;\r
 \r
 Error5:\r
+  //\r
+  // We would have inserted a TxToken only if Request structure is not NULL.\r
+  // Hence check before we do a remove in this error case.\r
+  //\r
+  if (Request != NULL) {\r
     NetMapRemoveTail (&HttpInstance->TxTokens, NULL);\r
+  }\r
 \r
 Error4:\r
-  if (RequestStr != NULL) {\r
-    FreePool (RequestStr);\r
+  if (RequestMsg != NULL) {\r
+    FreePool (RequestMsg);\r
   }  \r
 \r
 Error3:\r
-  HttpCloseConnection (HttpInstance);\r
-\r
+  if (HttpInstance->UseHttps) {\r
+    TlsCloseSession (HttpInstance);\r
+    TlsCloseTxRxEvent (HttpInstance);\r
+  }\r
 \r
 Error2:\r
-  HttpCloseTcp4ConnCloseEvent (HttpInstance);\r
-  if (NULL != Wrap->TcpWrap.TxToken.CompletionToken.Event) {\r
-    gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event);\r
-    Wrap->TcpWrap.TxToken.CompletionToken.Event = NULL;\r
+  HttpCloseConnection (HttpInstance);\r
+  \r
+  HttpCloseTcpConnCloseEvent (HttpInstance);\r
+  if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {\r
+    gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
+    Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;\r
+  }\r
+  if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {\r
+    gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);\r
+    Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;\r
   }\r
 \r
 Error1:\r
@@ -541,7 +678,7 @@ Error1:
 }\r
 \r
 /**\r
-  Cancel a TxToken or RxToken. \r
+  Cancel a user's Token. \r
  \r
   @param[in]  Map                The HTTP instance's token queue.\r
   @param[in]  Item               Object container for one HTTP token and token's wrap.\r
@@ -559,9 +696,9 @@ HttpCancelTokens (
   IN VOID                   *Context\r
   )\r
 {\r
-\r
   EFI_HTTP_TOKEN            *Token;\r
   HTTP_TOKEN_WRAP           *Wrap;\r
+  HTTP_PROTOCOL             *HttpInstance;\r
 \r
   Token = (EFI_HTTP_TOKEN *) Context;\r
 \r
@@ -575,26 +712,34 @@ HttpCancelTokens (
 \r
   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;\r
   ASSERT (Wrap != NULL);\r
-\r
-  //\r
-  // Free resources.\r
-  //\r
-  NetMapRemoveItem (Map, Item, NULL); \r
+  HttpInstance = Wrap->HttpInstance;\r
   \r
-  if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) {\r
-    gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event);\r
-  }\r
+  if (!HttpInstance->LocalAddressIsIPv6) {\r
+    if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
+      //\r
+      // Cancle the Token before close its Event.\r
+      //\r
+      HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);\r
 \r
-  if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
-    gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
-  }\r
+      //\r
+      // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+      //\r
+      DispatchDpc ();\r
+    }\r
+  } else {\r
+    if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
+      //\r
+      // Cancle the Token before close its Event.\r
+      //\r
+      HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);\r
 \r
-  if (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-    FreePool (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+      //\r
+      // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+      //\r
+      DispatchDpc ();\r
+    }\r
   }\r
 \r
-  FreePool (Wrap);\r
-\r
   //\r
   // If only one item is to be cancel, return EFI_ABORTED to stop\r
   // iterating the map any more.\r
@@ -642,22 +787,30 @@ HttpCancel (
     }\r
   }\r
 \r
-  //\r
-  // Then check the tokens queued by EfiHttpResponse().\r
-  //\r
-  Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
-  if (EFI_ERROR (Status)) {\r
-    if (Token != NULL) {\r
-      if (Status == EFI_ABORTED) {\r
-        return EFI_SUCCESS;\r
+  if (!HttpInstance->UseHttps) {\r
+    //\r
+    // Then check the tokens queued by EfiHttpResponse(), except for Https.\r
+    //\r
+    Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Token != NULL) {\r
+        if (Status == EFI_ABORTED) {\r
+          return EFI_SUCCESS;\r
+        } else {\r
+          return EFI_NOT_FOUND;\r
+        }\r
       } else {\r
-        return EFI_NOT_FOUND;\r
+        return Status;\r
       }\r
+    }\r
+  } else {\r
+    if (!HttpInstance->LocalAddressIsIPv6) {\r
+      HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);\r
     } else {\r
-      return Status;\r
+      HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);\r
     }\r
   }\r
-\r
+  \r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -679,8 +832,6 @@ HttpCancel (
   @retval EFI_SUCCESS             Request and Response queues are successfully flushed.\r
   @retval EFI_INVALID_PARAMETER   This is NULL.\r
   @retval EFI_NOT_STARTED         This instance hasn't been configured.\r
-  @retval EFI_NO_MAPPING          When using the default address, configuration (DHCP,\r
-                                  BOOTP, RARP, etc.) hasn't finished yet.\r
   @retval EFI_NOT_FOUND           The asynchronous request or response token is not\r
                                   found.\r
   @retval EFI_UNSUPPORTED         The implementation does not support this function.\r
@@ -734,6 +885,8 @@ HttpBodyParserCallback (
   )\r
 {\r
   HTTP_TOKEN_WRAP               *Wrap;\r
+  UINTN                         BodyLength;\r
+  CHAR8                         *Body;\r
 \r
   if (EventType != BodyParseEventOnComplete) {\r
     return EFI_SUCCESS;\r
@@ -744,10 +897,17 @@ HttpBodyParserCallback (
   }\r
 \r
   Wrap = (HTTP_TOKEN_WRAP *) Context;\r
-  Wrap->HttpInstance->NextMsg = Data;\r
+  Body = Wrap->HttpToken->Message->Body;\r
+  BodyLength = Wrap->HttpToken->Message->BodyLength;\r
+  if (Data < Body + BodyLength) {\r
+    Wrap->HttpInstance->NextMsg = Data;\r
+  } else {\r
+    Wrap->HttpInstance->NextMsg = NULL;\r
+  }\r
+  \r
 \r
   //\r
-  // Free TxToken since already received corrsponding HTTP response.\r
+  // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.\r
   //\r
   FreePool (Wrap);\r
 \r
@@ -761,7 +921,7 @@ HttpBodyParserCallback (
 \r
   @retval EFI_SUCCESS             Allocation succeeded.\r
   @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.\r
-  @retval EFI_NOT_READY           Can't find a corresponding TxToken or \r
+  @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or \r
                                   the EFI_HTTP_UTILITIES_PROTOCOL is not available.\r
 \r
 **/\r
@@ -772,12 +932,9 @@ HttpResponseWorker (
 {\r
   EFI_STATUS                    Status;\r
   EFI_HTTP_MESSAGE              *HttpMsg;\r
-  EFI_TCP4_IO_TOKEN             *RxToken;\r
-  EFI_TCP4_PROTOCOL             *Tcp4;\r
   CHAR8                         *EndofHeader;\r
   CHAR8                         *HttpHeaders;\r
   UINTN                         SizeofHeaders;\r
-  CHAR8                         *Buffer;\r
   UINTN                         BufferSize;\r
   UINTN                         StatusCode;\r
   CHAR8                         *Tmp;\r
@@ -789,6 +946,7 @@ HttpResponseWorker (
   NET_MAP_ITEM                  *Item;\r
   HTTP_TOKEN_WRAP               *ValueInItem;\r
   UINTN                         HdrLen;\r
+  NET_FRAGMENT                  Fragment;\r
 \r
   if (Wrap == NULL || Wrap->HttpInstance == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -796,27 +954,20 @@ HttpResponseWorker (
   \r
   HttpInstance = Wrap->HttpInstance;\r
   Token = Wrap->HttpToken;\r
-\r
   HttpMsg = Token->Message;\r
 \r
-  Tcp4 = HttpInstance->Tcp4;\r
-  ASSERT (Tcp4 != NULL);\r
-  HttpMsg->Headers = NULL;\r
-  HttpHeaders   = NULL;\r
-  SizeofHeaders = 0;\r
-  Buffer        = NULL;\r
-  BufferSize    = 0;\r
-  EndofHeader   = NULL;\r
+  HttpInstance->EndofHeader = NULL;\r
+  HttpInstance->HttpHeaders = NULL;\r
+  HttpMsg->Headers          = NULL;\r
+  HttpHeaders               = NULL;\r
+  SizeofHeaders             = 0;\r
+  BufferSize                = 0;\r
+  EndofHeader               = NULL;\r
+  ValueInItem               = NULL;\r
+  Fragment.Len              = 0;\r
+  Fragment.Bulk             = NULL;\r
  \r
   if (HttpMsg->Data.Response != NULL) {\r
-    //\r
-    // Need receive the HTTP headers, prepare buffer.\r
-    //\r
-    Status = HttpCreateTcp4RxEventForHeader (HttpInstance);\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error;\r
-    }\r
-\r
     //\r
     // Check whether we have cached header from previous call.\r
     //\r
@@ -843,70 +994,45 @@ HttpResponseWorker (
       // Check whether we cached the whole HTTP headers.\r
       //\r
       EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
-    }\r
-    \r
-    RxToken = &HttpInstance->RxToken;\r
-    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN);\r
-    if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) {\r
-      Status = EFI_OUT_OF_RESOURCES;\r
-      goto Error;\r
-    }\r
+    }   \r
 \r
-    //\r
-    // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL.\r
-    //\r
-    while (EndofHeader == NULL) {   \r
-      HttpInstance->IsRxDone = FALSE;\r
-      RxToken->Packet.RxData->DataLength = DEF_BUF_LEN;\r
-      RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN;\r
-      Status = Tcp4->Receive (Tcp4, RxToken);\r
-      if (EFI_ERROR (Status)) {\r
-        DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
-        goto Error;\r
-      }\r
-      \r
-      while (!HttpInstance->IsRxDone) {\r
-       Tcp4->Poll (Tcp4);\r
-      }    \r
+    HttpInstance->EndofHeader = &EndofHeader;\r
+    HttpInstance->HttpHeaders = &HttpHeaders;\r
 \r
-      Status = RxToken->CompletionToken.Status;\r
-      if (EFI_ERROR (Status)) {\r
-        goto Error;\r
-      }\r
 \r
+    if (HttpInstance->TimeoutEvent == NULL) {\r
       //\r
-      // Append the response string.\r
+      // Create TimeoutEvent for response\r
       //\r
-      BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength;\r
-      Buffer = AllocateZeroPool (BufferSize);\r
-      if (Buffer == NULL) {\r
-        Status = EFI_OUT_OF_RESOURCES;\r
+      Status = gBS->CreateEvent (\r
+                      EVT_TIMER,\r
+                      TPL_CALLBACK,\r
+                      NULL,\r
+                      NULL,\r
+                      &HttpInstance->TimeoutEvent\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
         goto Error;\r
       }\r
+    }\r
 \r
-      if (HttpHeaders != NULL) {\r
-        CopyMem (Buffer, HttpHeaders, SizeofHeaders);\r
-        FreePool (HttpHeaders);\r
-      }\r
+    //\r
+    // Start the timer, and wait Timeout seconds to receive the header packet.\r
+    //\r
+    Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
 \r
-      CopyMem (\r
-        Buffer + SizeofHeaders,\r
-        RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer,\r
-        RxToken->Packet.RxData->FragmentTable[0].FragmentLength\r
-        );\r
-      HttpHeaders   = Buffer;\r
-      SizeofHeaders = BufferSize;\r
+    Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);\r
 \r
-      //\r
-      // Check whether we received end of HTTP headers.\r
-      //\r
-      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
-    };\r
+    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
 \r
-    //\r
-    // Skip the CRLF after the HTTP headers.\r
-    //\r
-    EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    ASSERT (HttpHeaders != NULL);\r
 \r
     //\r
     // Cache the part of body.\r
@@ -927,14 +1053,12 @@ HttpResponseWorker (
       HttpInstance->CacheLen = BodyLen;\r
     }\r
 \r
-    FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
-    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
-\r
     //\r
     // Search for Status Code.\r
     //\r
     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
     if (StatusCodeStr == NULL) {\r
+      Status = EFI_NOT_READY;\r
       goto Error;\r
     }\r
 \r
@@ -945,100 +1069,127 @@ HttpResponseWorker (
     //\r
     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);\r
     if (Tmp == NULL) {\r
-      goto Error;\r
-    }\r
-\r
-    Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);\r
-    SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);\r
-    HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
-    if (HeaderTmp == NULL) {\r
-      goto Error;\r
-    }\r
-\r
-    CopyMem (HeaderTmp, Tmp, SizeofHeaders);\r
-    FreePool (HttpHeaders);\r
-    HttpHeaders = HeaderTmp;\r
-\r
-    //\r
-    // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.\r
-    //\r
-    if (mHttpUtilities == NULL) {\r
       Status = EFI_NOT_READY;\r
       goto Error;\r
     }\r
-    \r
+\r
     //\r
-    // Parse the HTTP header into array of key/value pairs.\r
+    // We could have response with just a HTTP message and no headers. For Example,\r
+    // "100 Continue". In such cases, we would not want to unnecessarily call a Parse\r
+    // method. A "\r\n" following Tmp string again would indicate an end. Compare and\r
+    // set SizeofHeaders to 0.\r
     //\r
-    Status = mHttpUtilities->Parse (\r
-                               mHttpUtilities, \r
-                               HttpHeaders, \r
-                               SizeofHeaders, \r
-                               &HttpMsg->Headers, \r
-                               &HttpMsg->HeaderCount\r
-                               );\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error;\r
+    Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);\r
+    if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {\r
+      SizeofHeaders = 0;\r
+    } else {\r
+      SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);\r
     }\r
 \r
-    FreePool (HttpHeaders);\r
-    HttpHeaders = NULL;\r
-    \r
     HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);\r
+    HttpInstance->StatusCode = StatusCode;\r
 \r
-    //\r
-    // Init message-body parser by header information.  \r
-    //\r
     Status = EFI_NOT_READY;\r
     ValueInItem = NULL;\r
-    NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);\r
-    if (ValueInItem == NULL)  {\r
-      goto Error;\r
-    }\r
 \r
     //\r
-    // The first TxToken not transmitted yet, insert back and return error.\r
+    // In cases of PUT/POST, after an initial request-response pair, we would do a\r
+    // continuous request without a response call. So, we would not do an insert of\r
+    // TxToken. After we have sent the complete file, we will call a response to get\r
+    // a final response from server. In such a case, we would not have any TxTokens.\r
+    // Hence, check that case before doing a NetMapRemoveHead.\r
     //\r
-    if (!ValueInItem->TcpWrap.IsTxDone) {\r
-      goto Error2;\r
-    }\r
+    if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {\r
+      NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);\r
+      if (ValueInItem == NULL)  {\r
+        goto Error;\r
+      }\r
 \r
-    Status = HttpInitMsgParser (\r
-               ValueInItem->TcpWrap.Method,\r
-               HttpMsg->Data.Response->StatusCode,\r
-               HttpMsg->HeaderCount,\r
-               HttpMsg->Headers,\r
-               HttpBodyParserCallback,\r
-               (VOID *) ValueInItem,\r
-               &HttpInstance->MsgParser\r
-               );\r
-    if (EFI_ERROR (Status)) {       \r
-      goto Error2;\r
+      //\r
+      // The first Tx Token not transmitted yet, insert back and return error.\r
+      //\r
+      if (!ValueInItem->TcpWrap.IsTxDone) {\r
+        goto Error2;\r
+      }\r
     }\r
 \r
-    //\r
-    // Check whether we received a complete HTTP message.\r
-    //\r
-    if (HttpInstance->CacheBody != NULL) {\r
-      Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);\r
+    if (SizeofHeaders != 0) {\r
+      HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
+      if (HeaderTmp == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Error2;\r
+      }\r
+\r
+      CopyMem (HeaderTmp, Tmp, SizeofHeaders);\r
+      FreePool (HttpHeaders);\r
+      HttpHeaders = HeaderTmp;\r
+\r
+      //\r
+      // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.\r
+      //\r
+      if (mHttpUtilities == NULL) {\r
+        Status = EFI_NOT_READY;\r
+        goto Error2;\r
+      }\r
+\r
+      //\r
+      // Parse the HTTP header into array of key/value pairs.\r
+      //\r
+      Status = mHttpUtilities->Parse (\r
+                                 mHttpUtilities,\r
+                                 HttpHeaders,\r
+                                 SizeofHeaders,\r
+                                 &HttpMsg->Headers,\r
+                                 &HttpMsg->HeaderCount\r
+                                 );\r
       if (EFI_ERROR (Status)) {\r
         goto Error2;\r
       }\r
 \r
-      if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
-        //\r
-        // Free the MsgParse since we already have a full HTTP message.\r
-        //\r
-        HttpFreeMsgParser (HttpInstance->MsgParser);\r
-        HttpInstance->MsgParser = NULL;\r
+      FreePool (HttpHeaders);\r
+      HttpHeaders = NULL;\r
+\r
+\r
+      //\r
+      // Init message-body parser by header information.\r
+      //\r
+      Status = HttpInitMsgParser (\r
+                 HttpInstance->Method,\r
+                 HttpMsg->Data.Response->StatusCode,\r
+                 HttpMsg->HeaderCount,\r
+                 HttpMsg->Headers,\r
+                 HttpBodyParserCallback,\r
+                 (VOID *) ValueInItem,\r
+                 &HttpInstance->MsgParser\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error2;\r
+      }\r
+\r
+      //\r
+      // Check whether we received a complete HTTP message.\r
+      //\r
+      if (HttpInstance->CacheBody != NULL) {\r
+        Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);\r
+        if (EFI_ERROR (Status)) {\r
+          goto Error2;\r
+        }\r
+\r
+        if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
+          //\r
+          // Free the MsgParse since we already have a full HTTP message.\r
+          //\r
+          HttpFreeMsgParser (HttpInstance->MsgParser);\r
+          HttpInstance->MsgParser = NULL;\r
+        }\r
       }\r
     }\r
 \r
-    if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {    \r
+    if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {\r
       Status = EFI_SUCCESS;\r
       goto Exit;\r
     }\r
-  }  \r
+  }\r
 \r
   //\r
   // Receive the response body.\r
@@ -1108,17 +1259,116 @@ HttpResponseWorker (
   //\r
   // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
   //\r
-  RxToken = &Wrap->TcpWrap.RxToken;\r
+  if (!HttpInstance->UseHttps) {\r
+    Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+    \r
+  } else {\r
+    if (HttpInstance->TimeoutEvent == NULL) {\r
+      //\r
+      // Create TimeoutEvent for response\r
+      //\r
+      Status = gBS->CreateEvent (\r
+                      EVT_TIMER,\r
+                      TPL_CALLBACK,\r
+                      NULL,\r
+                      NULL,\r
+                      &HttpInstance->TimeoutEvent\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error2;\r
+      }\r
+    }\r
 \r
-  RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength;\r
-  RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength;\r
-  RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body;\r
+    //\r
+    // Start the timer, and wait Timeout seconds to receive the body packet.\r
+    //\r
+    Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+  \r
+    Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);\r
 \r
-  RxToken->CompletionToken.Status = EFI_NOT_READY;\r
-  Status = Tcp4->Receive (Tcp4, RxToken);\r
-  if (EFI_ERROR (Status)) {\r
-    DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
-    goto Error;\r
+    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
+    \r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+\r
+    //\r
+    // Check whether we receive a complete HTTP message.\r
+    //\r
+    Status = HttpParseMessageBody (\r
+               HttpInstance->MsgParser,\r
+               (UINTN) Fragment.Len,\r
+               (CHAR8 *) Fragment.Bulk\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+\r
+    if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
+      //\r
+      // Free the MsgParse since we already have a full HTTP message.\r
+      //\r
+      HttpFreeMsgParser (HttpInstance->MsgParser);\r
+      HttpInstance->MsgParser = NULL;\r
+    }\r
+\r
+    //\r
+    // We receive part of header of next HTTP msg.\r
+    //\r
+    if (HttpInstance->NextMsg != NULL) {\r
+      HttpMsg->BodyLength = MIN ((UINTN) (HttpInstance->NextMsg - (CHAR8 *) Fragment.Bulk), HttpMsg->BodyLength);\r
+      CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+      \r
+      HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+      if (HttpInstance->CacheLen != 0) {\r
+        if (HttpInstance->CacheBody != NULL) {\r
+          FreePool (HttpInstance->CacheBody);\r
+        }\r
+        \r
+        HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+        if (HttpInstance->CacheBody == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Error2;\r
+        }\r
+        \r
+        CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+        HttpInstance->CacheOffset = 0;\r
+\r
+        HttpInstance->NextMsg = HttpInstance->CacheBody + (UINTN) (HttpInstance->NextMsg - (CHAR8 *) (Fragment.Bulk + HttpMsg->BodyLength));\r
+      }\r
+    } else {\r
+      HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);\r
+      CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+      HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+      if (HttpInstance->CacheLen != 0) {\r
+        if (HttpInstance->CacheBody != NULL) {\r
+          FreePool (HttpInstance->CacheBody);\r
+        }\r
+        \r
+        HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+        if (HttpInstance->CacheBody == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Error2;\r
+        }\r
+\r
+        CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+        HttpInstance->CacheOffset = 0;\r
+      }\r
+    }\r
+\r
+    if (Fragment.Bulk != NULL) {\r
+      FreePool (Fragment.Bulk);\r
+      Fragment.Bulk = NULL;\r
+    }\r
+\r
+    goto Exit;\r
   }\r
 \r
   return Status;\r
@@ -1128,44 +1378,48 @@ Exit:
   if (Item != NULL) {\r
     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
   }\r
-  Token->Status = Status;\r
+\r
+  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {\r
+    Token->Status = EFI_HTTP_ERROR;\r
+  } else {\r
+    Token->Status = Status;\r
+  }\r
+\r
   gBS->SignalEvent (Token->Event);\r
+  HttpCloseTcpRxEvent (Wrap);\r
   FreePool (Wrap);\r
   return Status;\r
 \r
 Error2:\r
-  NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
-\r
-Error:\r
-  if (Wrap != NULL) {\r
-    if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
-    }\r
-    RxToken = &Wrap->TcpWrap.RxToken;\r
-    if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-      FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
-      RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
-    }\r
-    FreePool (Wrap);\r
+  if (ValueInItem != NULL) {\r
+    NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
   }\r
 \r
-  if (HttpInstance->RxToken.CompletionToken.Event != NULL) {\r
-    gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event);\r
-    HttpInstance->RxToken.CompletionToken.Event = NULL;\r
+Error:\r
+  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);\r
+  if (Item != NULL) {\r
+    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
   }\r
 \r
-  RxToken = &HttpInstance->RxToken;\r
-  if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-    FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
-    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
+  if (!HttpInstance->UseHttps) {\r
+    HttpTcpTokenCleanup (Wrap);\r
+  } else {\r
+    FreePool (Wrap);\r
   }\r
   \r
   if (HttpHeaders != NULL) {\r
     FreePool (HttpHeaders);\r
+    HttpHeaders = NULL;\r
+  }\r
+\r
+  if (Fragment.Bulk != NULL) {\r
+    FreePool (Fragment.Bulk);\r
+    Fragment.Bulk = NULL;\r
   }\r
 \r
   if (HttpMsg->Headers != NULL) {\r
     FreePool (HttpMsg->Headers);\r
+    HttpMsg->Headers = NULL;\r
   }\r
 \r
   if (HttpInstance->CacheBody != NULL) {\r
@@ -1173,7 +1427,12 @@ Error:
     HttpInstance->CacheBody = NULL;\r
   }\r
 \r
-  Token->Status = Status;\r
+  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {\r
+    Token->Status = EFI_HTTP_ERROR;\r
+  } else {\r
+    Token->Status = Status;\r
+  }\r
+\r
   gBS->SignalEvent (Token->Event);\r
 \r
   return Status;  \r
@@ -1183,7 +1442,7 @@ Error:
 \r
 /**\r
   The Response() function queues an HTTP response to this HTTP instance, similar to\r
-  Receive() function in the EFI TCP driver. When the HTTP request is sent successfully,\r
+  Receive() function in the EFI TCP driver. When the HTTP response is received successfully,\r
   or if there is an error, Status in token will be updated and Event will be signaled.\r
 \r
   The HTTP driver will queue a receive token to the underlying TCP instance. When data\r
@@ -1256,10 +1515,6 @@ EfiHttpResponse (
     return EFI_NOT_STARTED;\r
   }\r
 \r
-  if (HttpInstance->LocalAddressIsIPv6) {\r
-    return EFI_UNSUPPORTED;\r
-  }  \r
-\r
   //\r
   // Check whether the token already existed.\r
   //\r
@@ -1275,9 +1530,16 @@ EfiHttpResponse (
   Wrap->HttpInstance = HttpInstance;\r
   Wrap->HttpToken    = Token;\r
 \r
-  Status = HttpCreateTcp4RxEvent (Wrap);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error;\r
+  //\r
+  // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to \r
+  // receive the https response. A special TlsRxToken is used for receiving TLS \r
+  // related messages. It should be a blocking response.\r
+  //\r
+  if (!HttpInstance->UseHttps) {\r
+    Status = HttpCreateTcpRxEvent (Wrap);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
   }\r
 \r
   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);\r
@@ -1296,8 +1558,12 @@ EfiHttpResponse (
 \r
 Error:\r
   if (Wrap != NULL) {\r
-    if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
+    if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
+      gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
+    }\r
+\r
+    if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
+      gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
     }\r
     FreePool (Wrap);\r
   }  \r
@@ -1331,8 +1597,8 @@ EfiHttpPoll (
   IN  EFI_HTTP_PROTOCOL         *This\r
   )\r
 {\r
-  HTTP_PROTOCOL                 *HttpInstance;\r
   EFI_STATUS                    Status;\r
+  HTTP_PROTOCOL                 *HttpInstance;\r
 \r
   if (This == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -1341,17 +1607,23 @@ EfiHttpPoll (
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
   ASSERT (HttpInstance != NULL);\r
 \r
-  if (HttpInstance->LocalAddressIsIPv6) {\r
-    return EFI_UNSUPPORTED;\r
-  }\r
-\r
-  if (HttpInstance->Tcp4 == NULL || HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
+  if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
     return EFI_NOT_STARTED;\r
   }\r
-\r
-  Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);\r
-\r
+  \r
+  if (HttpInstance->LocalAddressIsIPv6) {\r
+    if (HttpInstance->Tcp6 == NULL) {\r
+      return EFI_NOT_STARTED;\r
+    }\r
+    Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);\r
+  } else {\r
+    if (HttpInstance->Tcp4 == NULL) {\r
+      return EFI_NOT_STARTED;\r
+    }\r
+    Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);\r
+  }\r
+  \r
   DispatchDpc ();\r
-\r
\r
   return Status;\r
 }\r