]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpDxe/HttpImpl.c
NetworkPkg/HttpDxe: Fix the bug when parsing HTTP(S) message body.
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpImpl.c
index 553b79cca01aeb35805398e7168fe70c6920a638..17deceb395473e7457e2c3616c7059eea423cc56 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.\r
 \r
-  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2015 - 2018, 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
@@ -33,16 +33,18 @@ EFI_HTTP_PROTOCOL  mEfiHttpTemplate = {
 \r
   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.\r
   @param[out] HttpConfigData      Point to buffer for operational parameters of this\r
-                                  HTTP instance.\r
+                                  HTTP instance. It is the responsibility of the caller\r
+                                  to allocate the memory for HttpConfigData and\r
+                                  HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact,\r
+                                  it is recommended to allocate sufficient memory to record\r
+                                  IPv6Node since it is big enough for all possibilities.\r
 \r
   @retval EFI_SUCCESS             Operation succeeded.\r
   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:\r
                                   This is NULL.\r
                                   HttpConfigData is NULL.\r
-                                  HttpInstance->LocalAddressIsIPv6 is FALSE and\r
-                                  HttpConfigData->IPv4Node is NULL.\r
-                                  HttpInstance->LocalAddressIsIPv6 is TRUE and\r
-                                  HttpConfigData->IPv6Node is NULL.\r
+                                  HttpConfigData->AccessPoint.IPv4Node or\r
+                                  HttpConfigData->AccessPoint.IPv6Node is NULL.\r
   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.\r
 \r
 **/\r
@@ -63,10 +65,9 @@ EfiHttpGetModeData (
   }\r
 \r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
-  ASSERT (HttpInstance != NULL);\r
 \r
-  if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
-      (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
+  if ((HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
+      (HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -114,11 +115,10 @@ EfiHttpGetModeData (
   @retval EFI_SUCCESS             Operation succeeded.\r
   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:\r
                                   This is NULL.\r
-                                  HttpConfigData is NULL.\r
                                   HttpConfigData->LocalAddressIsIPv6 is FALSE and\r
-                                  HttpConfigData->IPv4Node is NULL.\r
+                                  HttpConfigData->AccessPoint.IPv4Node is NULL.\r
                                   HttpConfigData->LocalAddressIsIPv6 is TRUE and\r
-                                  HttpConfigData->IPv6Node is NULL.\r
+                                  HttpConfigData->AccessPoint.IPv6Node is NULL.\r
   @retval EFI_ALREADY_STARTED     Reinitialize this HTTP instance without calling\r
                                   Configure() with NULL to reset it.\r
   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.\r
@@ -131,27 +131,31 @@ EFI_STATUS
 EFIAPI\r
 EfiHttpConfigure (\r
   IN  EFI_HTTP_PROTOCOL         *This,\r
-  IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData\r
-  ) \r
+  IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData OPTIONAL\r
+  )\r
 {\r
   HTTP_PROTOCOL                 *HttpInstance;\r
   EFI_STATUS                    Status;\r
-  \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
+      (HttpConfigData != NULL &&\r
+       ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
+        (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
-  ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);\r
+  ASSERT (HttpInstance->Service != NULL);\r
 \r
   if (HttpConfigData != NULL) {\r
 \r
+    if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
     //\r
     // Now configure this HTTP instance.\r
     //\r
@@ -162,8 +166,8 @@ EfiHttpConfigure (
     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;\r
     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;\r
     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;\r
-    \r
-    if (HttpConfigData->LocalAddressIsIPv6) { \r
+\r
+    if (HttpConfigData->LocalAddressIsIPv6) {\r
       CopyMem (\r
         &HttpInstance->Ipv6Node,\r
         HttpConfigData->AccessPoint.IPv6Node,\r
@@ -176,6 +180,7 @@ EfiHttpConfigure (
         sizeof (HttpInstance->IPv4Node)\r
         );\r
     }\r
+\r
     //\r
     // Creat Tcp child\r
     //\r
@@ -183,7 +188,7 @@ EfiHttpConfigure (
     if (EFI_ERROR (Status)) {\r
       return Status;\r
     }\r
-    \r
+\r
     HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;\r
     return EFI_SUCCESS;\r
 \r
@@ -196,7 +201,7 @@ EfiHttpConfigure (
     return EFI_SUCCESS;\r
   }\r
 }\r
\r
+\r
 \r
 /**\r
   The Request() function queues an HTTP request to this HTTP instance.\r
@@ -236,10 +241,12 @@ 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
+  BOOLEAN                       TlsConfigure;\r
   CHAR8                         *RequestMsg;\r
   CHAR8                         *Url;\r
   UINTN                         UrlLen;\r
@@ -247,154 +254,248 @@ EfiHttpRequest (
   HTTP_TOKEN_WRAP               *Wrap;\r
   CHAR8                         *FileUrl;\r
   UINTN                         RequestMsgSize;\r
-  \r
+  EFI_HANDLE                    ImageHandle;\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, DELETE, PATCH, 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 != HttpMethodDelete) &&\r
+      (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost) &&\r
+      (Request->Method != HttpMethodPatch)) {\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
-  //\r
-  // Check whether the token already existed.\r
-  //\r
-  if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {\r
-    return EFI_ACCESS_DENIED;   \r
-  }  \r
+  if (Request == NULL) {\r
+    //\r
+    // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation)\r
+    //\r
+    if ((HttpInstance->Method != HttpMethodPut) &&\r
+        (HttpInstance->Method != HttpMethodPost) &&\r
+        (HttpInstance->Method != HttpMethodPatch)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  HostName    = NULL;\r
-  Wrap        = NULL;\r
-  HostNameStr = NULL;\r
+    //\r
+    // For PUT/POST/PATCH, 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
-  //\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
+    // 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
-    FreePool (HttpInstance->Url);\r
-    HttpInstance->Url = Url;    \r
-  } \r
 \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
 \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
-  RequestMsg = 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
-  //\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
+    UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);\r
 \r
-  if (HttpInstance->RemoteHost == NULL) {\r
     //\r
-    // Request() is called the first time. \r
+    // From the information in Url, the HTTP instance will\r
+    // be able to determine whether to use http or https.\r
     //\r
-    ReConfigure = FALSE;\r
-  } else {\r
-    if ((HttpInstance->RemotePort == RemotePort) &&\r
-        (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
+    HttpInstance->UseHttps = IsHttpsUrl (Url);\r
+\r
+    //\r
+    // HTTP is disabled, return directly if the URI is not HTTPS.\r
+    //\r
+    if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {\r
+\r
+      DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));\r
+\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+\r
+    //\r
+    // Check whether we need to create Tls child and open the TLS protocol.\r
+    //\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
+      if (HttpInstance->LocalAddressIsIPv6) {\r
+        ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;\r
+      } else {\r
+        ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;\r
+      }\r
 \r
-        Wrap->HttpToken    = Token;\r
-        Wrap->HttpInstance = HttpInstance;\r
+      HttpInstance->TlsChildHandle = TlsCreateChild (\r
+                                       ImageHandle,\r
+                                       &(HttpInstance->TlsSb),\r
+                                       &(HttpInstance->Tls),\r
+                                       &(HttpInstance->TlsConfiguration)\r
+                                       );\r
+      if (HttpInstance->TlsChildHandle == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
 \r
-        Status = HttpCreateTcpTxEvent (Wrap);\r
-        if (EFI_ERROR (Status)) {\r
-          goto Error1;\r
-        }\r
+      TlsConfigure = TRUE;\r
+    }\r
 \r
-        Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, 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
-        Wrap->TcpWrap.Method = Request->Method;\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
+      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
+          HttpUrlFreeParser (UrlParser);\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
-        HttpInstance->RemotePort = 0;\r
+        if (HttpInstance->RemoteHost != NULL) {\r
+          FreePool (HttpInstance->RemoteHost);\r
+          HttpInstance->RemoteHost = NULL;\r
+          HttpInstance->RemotePort = 0;\r
+        }\r
       }\r
     }\r
-  } \r
+  }\r
 \r
   if (Configure) {\r
     //\r
@@ -407,26 +508,27 @@ EfiHttpRequest (
     }\r
 \r
     if (EFI_ERROR (Status)) {\r
-      HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));\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
+\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
+\r
       FreePool (HostNameStr);\r
       if (EFI_ERROR (Status)) {\r
+        DEBUG ((EFI_D_ERROR, "Error: Could not retrieve the host address from DNS server.\n"));\r
         goto Error1;\r
       }\r
     }\r
 \r
-\r
     //\r
     // Save the RemotePort and RemoteHost.\r
     //\r
@@ -445,6 +547,16 @@ EfiHttpRequest (
     } 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
@@ -460,28 +572,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 = HttpInitTcp (HttpInstance, Wrap, Configure);\r
+  Status = HttpInitSession (\r
+             HttpInstance,\r
+             Wrap,\r
+             Configure || ReConfigure,\r
+             TlsConfigure\r
+             );\r
   if (EFI_ERROR (Status)) {\r
     goto Error2;\r
-  }  \r
+  }\r
 \r
-  if (!Configure) {\r
+  if (!Configure && !ReConfigure && !TlsConfigure) {\r
     //\r
-    // For the new HTTP token, create TX TCP token events.    \r
+    // For the new HTTP token, create TX TCP token events.\r
     //\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
@@ -501,13 +620,21 @@ EfiHttpRequest (
 \r
   Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);\r
 \r
-  if (EFI_ERROR (Status)) {\r
+  if (EFI_ERROR (Status) || NULL == RequestMsg) {\r
     goto Error3;\r
   }\r
 \r
-  Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error4;\r
+  //\r
+  // Every request we insert a TxToken and a response call would remove the TxToken.\r
+  // In cases of PUT/POST/PATCH, 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
@@ -520,29 +647,44 @@ EfiHttpRequest (
              RequestMsgSize\r
              );\r
   if (EFI_ERROR (Status)) {\r
-    goto Error5;    \r
+    goto Error5;\r
   }\r
 \r
   DispatchDpc ();\r
-  \r
+\r
   if (HostName != NULL) {\r
     FreePool (HostName);\r
   }\r
-  \r
+\r
+  if (UrlParser != NULL) {\r
+    HttpUrlFreeParser (UrlParser);\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 (RequestMsg != NULL) {\r
     FreePool (RequestMsg);\r
-  }  \r
+  }\r
 \r
 Error3:\r
-  HttpCloseConnection (HttpInstance);\r
+  if (HttpInstance->UseHttps) {\r
+    TlsCloseSession (HttpInstance);\r
+    TlsCloseTxRxEvent (HttpInstance);\r
+  }\r
 \r
 Error2:\r
+  HttpCloseConnection (HttpInstance);\r
+\r
   HttpCloseTcpConnCloseEvent (HttpInstance);\r
   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {\r
     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
@@ -554,24 +696,23 @@ Error2:
   }\r
 \r
 Error1:\r
-\r
   if (HostName != NULL) {\r
     FreePool (HostName);\r
   }\r
   if (Wrap != NULL) {\r
     FreePool (Wrap);\r
   }\r
-  if (UrlParser!= NULL) {\r
+  if (UrlParser != NULL) {\r
     HttpUrlFreeParser (UrlParser);\r
   }\r
 \r
   return Status;\r
-  \r
+\r
 }\r
 \r
 /**\r
-  Cancel a user's Token. \r
\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
   @param[in]  Context            The user's token to cancel.\r
@@ -588,7 +729,6 @@ HttpCancelTokens (
   IN VOID                   *Context\r
   )\r
 {\r
-\r
   EFI_HTTP_TOKEN            *Token;\r
   HTTP_TOKEN_WRAP           *Wrap;\r
   HTTP_PROTOCOL             *HttpInstance;\r
@@ -607,41 +747,32 @@ HttpCancelTokens (
   ASSERT (Wrap != NULL);\r
   HttpInstance = Wrap->HttpInstance;\r
 \r
-  //\r
-  // Free resources.\r
-  //\r
-  NetMapRemoveItem (Map, Item, NULL); \r
-  \r
   if (!HttpInstance->LocalAddressIsIPv6) {\r
-    if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
-    }\r
-    \r
     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
-    }\r
-    \r
-    if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-      FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
-    }\r
+      //\r
+      // Cancle the Token before close its Event.\r
+      //\r
+      HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);\r
 \r
-  } else {\r
-    if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);\r
+      //\r
+      // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+      //\r
+      DispatchDpc ();\r
     }\r
-\r
+  } else {\r
     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
-    }\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.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-      FreePool (Wrap->TcpWrap.Rx6Token.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
-\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
@@ -650,7 +781,7 @@ HttpCancelTokens (
     return EFI_ABORTED;\r
   }\r
 \r
-  return EFI_SUCCESS; \r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -663,7 +794,7 @@ HttpCancelTokens (
                                  cancelled.\r
 \r
   @retval EFI_SUCCESS            The token is cancelled.\r
-  @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.                                 \r
+  @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.\r
   @retval Others                 Other error as indicated.\r
 \r
 **/\r
@@ -683,25 +814,33 @@ HttpCancel (
     if (Token != NULL) {\r
       if (Status == EFI_ABORTED) {\r
         return EFI_SUCCESS;\r
-      } \r
+      }\r
     } else {\r
       return Status;\r
     }\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
@@ -745,7 +884,6 @@ EfiHttpCancel (
   }\r
 \r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
-  ASSERT (HttpInstance != NULL);\r
 \r
   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
     return EFI_NOT_STARTED;\r
@@ -778,6 +916,7 @@ HttpBodyParserCallback (
   IN VOID                       *Context\r
   )\r
 {\r
+  HTTP_CALLBACK_DATA            *CallbackData;\r
   HTTP_TOKEN_WRAP               *Wrap;\r
   UINTN                         BodyLength;\r
   CHAR8                         *Body;\r
@@ -790,20 +929,17 @@ HttpBodyParserCallback (
     return EFI_SUCCESS;\r
   }\r
 \r
-  Wrap = (HTTP_TOKEN_WRAP *) Context;\r
-  Body = Wrap->HttpToken->Message->Body;\r
-  BodyLength = Wrap->HttpToken->Message->BodyLength;\r
+  CallbackData = (HTTP_CALLBACK_DATA *) Context;\r
+\r
+  Wrap       = (HTTP_TOKEN_WRAP *) (CallbackData->Wrap);\r
+  Body       = CallbackData->ParseData;\r
+  BodyLength = CallbackData->ParseDataLength;\r
+\r
   if (Data < Body + BodyLength) {\r
     Wrap->HttpInstance->NextMsg = Data;\r
   } else {\r
     Wrap->HttpInstance->NextMsg = NULL;\r
   }\r
-  \r
-\r
-  //\r
-  // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.\r
-  //\r
-  FreePool (Wrap);\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -815,7 +951,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 Tx4Token/Tx6Token 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
@@ -840,11 +976,12 @@ 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
   }\r
-  \r
+\r
   HttpInstance = Wrap->HttpInstance;\r
   Token = Wrap->HttpToken;\r
   HttpMsg = Token->Message;\r
@@ -856,16 +993,11 @@ HttpResponseWorker (
   SizeofHeaders             = 0;\r
   BufferSize                = 0;\r
   EndofHeader               = NULL;\r
\r
-  if (HttpMsg->Data.Response != NULL) {\r
-    //\r
-    // Need receive the HTTP headers, prepare buffer.\r
-    //\r
-    Status = HttpCreateTcpRxEventForHeader (HttpInstance);\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error;\r
-    }\r
+  ValueInItem               = NULL;\r
+  Fragment.Len              = 0;\r
+  Fragment.Bulk             = NULL;\r
 \r
+  if (HttpMsg->Data.Response != NULL) {\r
     //\r
     // Check whether we have cached header from previous call.\r
     //\r
@@ -891,13 +1023,41 @@ HttpResponseWorker (
       //\r
       // Check whether we cached the whole HTTP headers.\r
       //\r
-      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
-    }   \r
+      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR);\r
+    }\r
 \r
     HttpInstance->EndofHeader = &EndofHeader;\r
     HttpInstance->HttpHeaders = &HttpHeaders;\r
 \r
-    Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize);\r
+\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 Error;\r
+      }\r
+    }\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
+    Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);\r
+\r
+    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
+\r
     if (EFI_ERROR (Status)) {\r
       goto Error;\r
     }\r
@@ -928,6 +1088,7 @@ HttpResponseWorker (
     //\r
     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
     if (StatusCodeStr == NULL) {\r
+      Status = EFI_NOT_READY;\r
       goto Error;\r
     }\r
 \r
@@ -938,86 +1099,123 @@ 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
-    // Init message-body parser by header information.  \r
-    //\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 Tx Token not transmitted yet, insert back and return error.\r
+    // In cases of PUT/POST/PATCH, 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
+      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 *) (&HttpInstance->CallbackData),\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
+        //\r
+        // Record the CallbackData data.\r
+        //\r
+        HttpInstance->CallbackData.Wrap = (VOID *) Wrap;\r
+        HttpInstance->CallbackData.ParseData = (VOID *) HttpInstance->CacheBody;\r
+        HttpInstance->CallbackData.ParseDataLength = HttpInstance->CacheLen;\r
+\r
+        //\r
+        // Parse message with CallbackData data.\r
+        //\r
+        Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);\r
+        if (EFI_ERROR (Status)) {\r
+          goto Error2;\r
+        }\r
+      }\r
+\r
       if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
         //\r
         // Free the MsgParse since we already have a full HTTP message.\r
@@ -1027,11 +1225,11 @@ HttpResponseWorker (
       }\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
@@ -1049,7 +1247,7 @@ HttpResponseWorker (
       //\r
       // We have a cached HTTP message which includes a part of HTTP header of next message.\r
       //\r
-      BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);      \r
+      BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);\r
     } else {\r
       BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;\r
     }\r
@@ -1084,7 +1282,7 @@ HttpResponseWorker (
       //\r
       Status = EFI_SUCCESS;\r
       goto Exit;\r
-    } \r
+    }\r
 \r
     if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {\r
       //\r
@@ -1092,8 +1290,8 @@ HttpResponseWorker (
       //\r
       HttpMsg->BodyLength = 0;\r
       Status = EFI_SUCCESS;\r
-      goto Exit;      \r
-    }    \r
+      goto Exit;\r
+    }\r
   }\r
 \r
   ASSERT (HttpInstance->MsgParser != NULL);\r
@@ -1101,9 +1299,112 @@ HttpResponseWorker (
   //\r
   // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
   //\r
-  Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error;\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
+    //\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
+    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+\r
+    //\r
+    // Process the received the body packet.\r
+    //\r
+    HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);\r
+\r
+    CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+\r
+    //\r
+    // Record the CallbackData data.\r
+    //\r
+    HttpInstance->CallbackData.Wrap = (VOID *) Wrap;\r
+    HttpInstance->CallbackData.ParseData = HttpMsg->Body;\r
+    HttpInstance->CallbackData.ParseDataLength = HttpMsg->BodyLength;\r
+\r
+    //\r
+    // Parse Body with CallbackData data.\r
+    //\r
+    Status = HttpParseMessageBody (\r
+               HttpInstance->MsgParser,\r
+               HttpMsg->BodyLength,\r
+               HttpMsg->Body\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
+    // Check whether there is the next message header in the HttpMsg->Body.\r
+    //\r
+    if (HttpInstance->NextMsg != NULL) {\r
+      HttpMsg->BodyLength = HttpInstance->NextMsg - (CHAR8 *) HttpMsg->Body;\r
+    }\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
+      if (HttpInstance->NextMsg != NULL) {\r
+        HttpInstance->NextMsg = HttpInstance->CacheBody;\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
@@ -1126,17 +1427,35 @@ Exit:
   return Status;\r
 \r
 Error2:\r
-  NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
+  if (ValueInItem != NULL) {\r
+    NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
+  }\r
 \r
 Error:\r
-  HttpTcpTokenCleanup (Wrap);\r
-  \r
+  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);\r
+  if (Item != NULL) {\r
+    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
+  }\r
+\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
@@ -1152,7 +1471,7 @@ Error:
 \r
   gBS->SignalEvent (Token->Event);\r
 \r
-  return Status;  \r
+  return Status;\r
 \r
 }\r
 \r
@@ -1224,9 +1543,8 @@ EfiHttpResponse (
   if (HttpMsg == 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_TCP_CONNECTED) {\r
     return EFI_NOT_STARTED;\r
@@ -1236,7 +1554,7 @@ EfiHttpResponse (
   // Check whether the token already existed.\r
   //\r
   if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {\r
-    return EFI_ACCESS_DENIED;   \r
+    return EFI_ACCESS_DENIED;\r
   }\r
 \r
   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
@@ -1247,9 +1565,16 @@ EfiHttpResponse (
   Wrap->HttpInstance = HttpInstance;\r
   Wrap->HttpToken    = Token;\r
 \r
-  Status = HttpCreateTcpRxEvent (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
@@ -1276,9 +1601,9 @@ Error:
       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
     }\r
     FreePool (Wrap);\r
-  }  \r
+  }\r
 \r
-  return Status;  \r
+  return Status;\r
 }\r
 \r
 /**\r
@@ -1315,12 +1640,11 @@ EfiHttpPoll (
   }\r
 \r
   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
-  ASSERT (HttpInstance != NULL);\r
 \r
   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
     return EFI_NOT_STARTED;\r
   }\r
-  \r
+\r
   if (HttpInstance->LocalAddressIsIPv6) {\r
     if (HttpInstance->Tcp6 == NULL) {\r
       return EFI_NOT_STARTED;\r
@@ -1332,8 +1656,8 @@ EfiHttpPoll (
     }\r
     Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);\r
   }\r
-  \r
+\r
   DispatchDpc ();\r
\r
+\r
   return Status;\r
 }\r