Home | History | Annotate | Download | only in HttpDxe
      1 /** @file
      2   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
      3 
      4   Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
      5   (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
      6 
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php.
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include "HttpDriver.h"
     18 
     19 EFI_HTTP_PROTOCOL  mEfiHttpTemplate = {
     20   EfiHttpGetModeData,
     21   EfiHttpConfigure,
     22   EfiHttpRequest,
     23   EfiHttpCancel,
     24   EfiHttpResponse,
     25   EfiHttpPoll
     26 };
     27 
     28 /**
     29   Returns the operational parameters for the current HTTP child instance.
     30 
     31   The GetModeData() function is used to read the current mode data (operational
     32   parameters) for this HTTP protocol instance.
     33 
     34   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
     35   @param[out] HttpConfigData      Point to buffer for operational parameters of this
     36                                   HTTP instance.
     37 
     38   @retval EFI_SUCCESS             Operation succeeded.
     39   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
     40                                   This is NULL.
     41                                   HttpConfigData is NULL.
     42                                   HttpInstance->LocalAddressIsIPv6 is FALSE and
     43                                   HttpConfigData->IPv4Node is NULL.
     44                                   HttpInstance->LocalAddressIsIPv6 is TRUE and
     45                                   HttpConfigData->IPv6Node is NULL.
     46   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
     47 
     48 **/
     49 EFI_STATUS
     50 EFIAPI
     51 EfiHttpGetModeData (
     52   IN  EFI_HTTP_PROTOCOL         *This,
     53   OUT EFI_HTTP_CONFIG_DATA      *HttpConfigData
     54   )
     55 {
     56   HTTP_PROTOCOL                 *HttpInstance;
     57 
     58   //
     59   // Check input parameters.
     60   //
     61   if ((This == NULL) || (HttpConfigData == NULL)) {
     62     return EFI_INVALID_PARAMETER;
     63   }
     64 
     65   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
     66   ASSERT (HttpInstance != NULL);
     67 
     68   if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
     69       (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {
     70     return EFI_INVALID_PARAMETER;
     71   }
     72 
     73   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
     74     return EFI_NOT_STARTED;
     75   }
     76 
     77   HttpConfigData->HttpVersion        = HttpInstance->HttpVersion;
     78   HttpConfigData->TimeOutMillisec    = HttpInstance->TimeOutMillisec;
     79   HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;
     80 
     81   if (HttpInstance->LocalAddressIsIPv6) {
     82     CopyMem (
     83       HttpConfigData->AccessPoint.IPv6Node,
     84       &HttpInstance->Ipv6Node,
     85       sizeof (HttpInstance->Ipv6Node)
     86     );
     87   } else {
     88     CopyMem (
     89       HttpConfigData->AccessPoint.IPv4Node,
     90       &HttpInstance->IPv4Node,
     91       sizeof (HttpInstance->IPv4Node)
     92       );
     93   }
     94 
     95   return EFI_SUCCESS;
     96 }
     97 
     98 /**
     99   Initialize or brutally reset the operational parameters for this EFI HTTP instance.
    100 
    101   The Configure() function does the following:
    102   When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring
    103   timeout, local address, port, etc.
    104   When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active
    105   connections with remote hosts, canceling all asynchronous tokens, and flush request
    106   and response buffers without informing the appropriate hosts.
    107 
    108   No other EFI HTTP function can be executed by this instance until the Configure()
    109   function is executed and returns successfully.
    110 
    111   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
    112   @param[in]  HttpConfigData      Pointer to the configure data to configure the instance.
    113 
    114   @retval EFI_SUCCESS             Operation succeeded.
    115   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
    116                                   This is NULL.
    117                                   HttpConfigData->LocalAddressIsIPv6 is FALSE and
    118                                   HttpConfigData->IPv4Node is NULL.
    119                                   HttpConfigData->LocalAddressIsIPv6 is TRUE and
    120                                   HttpConfigData->IPv6Node is NULL.
    121   @retval EFI_ALREADY_STARTED     Reinitialize this HTTP instance without calling
    122                                   Configure() with NULL to reset it.
    123   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
    124   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources when
    125                                   executing Configure().
    126   @retval EFI_UNSUPPORTED         One or more options in HttpConfigData are not supported
    127                                   in the implementation.
    128 **/
    129 EFI_STATUS
    130 EFIAPI
    131 EfiHttpConfigure (
    132   IN  EFI_HTTP_PROTOCOL         *This,
    133   IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData
    134   )
    135 {
    136   HTTP_PROTOCOL                 *HttpInstance;
    137   EFI_STATUS                    Status;
    138 
    139   //
    140   // Check input parameters.
    141   //
    142   if (This == NULL ||
    143       (HttpConfigData != NULL &&
    144        ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
    145         (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {
    146     return EFI_INVALID_PARAMETER;
    147   }
    148 
    149   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    150   ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);
    151 
    152   if (HttpConfigData != NULL) {
    153 
    154     //
    155     // Now configure this HTTP instance.
    156     //
    157     if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {
    158       return EFI_ALREADY_STARTED;
    159     }
    160 
    161     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;
    162     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;
    163     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
    164 
    165     if (HttpConfigData->LocalAddressIsIPv6) {
    166       CopyMem (
    167         &HttpInstance->Ipv6Node,
    168         HttpConfigData->AccessPoint.IPv6Node,
    169         sizeof (HttpInstance->Ipv6Node)
    170         );
    171     } else {
    172       CopyMem (
    173         &HttpInstance->IPv4Node,
    174         HttpConfigData->AccessPoint.IPv4Node,
    175         sizeof (HttpInstance->IPv4Node)
    176         );
    177     }
    178 
    179     //
    180     // Creat Tcp child
    181     //
    182     Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6);
    183     if (EFI_ERROR (Status)) {
    184       return Status;
    185     }
    186 
    187     HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;
    188     return EFI_SUCCESS;
    189 
    190   } else {
    191     //
    192     // Reset all the resources related to HttpInsance.
    193     //
    194     HttpCleanProtocol (HttpInstance);
    195     HttpInstance->State = HTTP_STATE_UNCONFIGED;
    196     return EFI_SUCCESS;
    197   }
    198 }
    199 
    200 
    201 /**
    202   The Request() function queues an HTTP request to this HTTP instance.
    203 
    204   Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent
    205   successfully, or if there is an error, Status in token will be updated and Event will
    206   be signaled.
    207 
    208   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
    209   @param[in]  Token               Pointer to storage containing HTTP request token.
    210 
    211   @retval EFI_SUCCESS             Outgoing data was processed.
    212   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
    213   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
    214   @retval EFI_TIMEOUT             Data was dropped out of the transmit or receive queue.
    215   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
    216   @retval EFI_UNSUPPORTED         The HTTP method is not supported in current
    217                                   implementation.
    218   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
    219                                   This is NULL.
    220                                   Token is NULL.
    221                                   Token->Message is NULL.
    222                                   Token->Message->Body is not NULL,
    223                                   Token->Message->BodyLength is non-zero, and
    224                                   Token->Message->Data is NULL, but a previous call to
    225                                   Request()has not been completed successfully.
    226 **/
    227 EFI_STATUS
    228 EFIAPI
    229 EfiHttpRequest (
    230   IN  EFI_HTTP_PROTOCOL         *This,
    231   IN  EFI_HTTP_TOKEN            *Token
    232   )
    233 {
    234   EFI_HTTP_MESSAGE              *HttpMsg;
    235   EFI_HTTP_REQUEST_DATA         *Request;
    236   VOID                          *UrlParser;
    237   EFI_STATUS                    Status;
    238   CHAR8                         *HostName;
    239   UINTN                         HostNameSize;
    240   UINT16                        RemotePort;
    241   HTTP_PROTOCOL                 *HttpInstance;
    242   BOOLEAN                       Configure;
    243   BOOLEAN                       ReConfigure;
    244   BOOLEAN                       TlsConfigure;
    245   CHAR8                         *RequestMsg;
    246   CHAR8                         *Url;
    247   UINTN                         UrlLen;
    248   CHAR16                        *HostNameStr;
    249   HTTP_TOKEN_WRAP               *Wrap;
    250   CHAR8                         *FileUrl;
    251   UINTN                         RequestMsgSize;
    252 
    253   //
    254   // Initializations
    255   //
    256   Url = NULL;
    257   UrlParser = NULL;
    258   RemotePort = 0;
    259   HostName = NULL;
    260   RequestMsg = NULL;
    261   HostNameStr = NULL;
    262   Wrap = NULL;
    263   FileUrl = NULL;
    264   TlsConfigure = FALSE;
    265 
    266   if ((This == NULL) || (Token == NULL)) {
    267     return EFI_INVALID_PARAMETER;
    268   }
    269 
    270   HttpMsg = Token->Message;
    271   if (HttpMsg == NULL) {
    272     return EFI_INVALID_PARAMETER;
    273   }
    274 
    275   Request = HttpMsg->Data.Request;
    276 
    277   //
    278   // Only support GET, HEAD, PUT and POST method in current implementation.
    279   //
    280   if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
    281       (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost)) {
    282     return EFI_UNSUPPORTED;
    283   }
    284 
    285   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    286   ASSERT (HttpInstance != NULL);
    287 
    288   //
    289   // Capture the method into HttpInstance.
    290   //
    291   if (Request != NULL) {
    292     HttpInstance->Method = Request->Method;
    293   }
    294 
    295   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
    296     return EFI_NOT_STARTED;
    297   }
    298 
    299   if (Request == NULL) {
    300     //
    301     // Request would be NULL only for PUT/POST operation (in the current implementation)
    302     //
    303     if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {
    304       return EFI_INVALID_PARAMETER;
    305     }
    306 
    307     //
    308     // For PUT/POST, we need to have the TCP already configured. Bail out if it is not!
    309     //
    310     if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
    311       return EFI_INVALID_PARAMETER;
    312     }
    313 
    314     //
    315     // We need to have the Message Body for sending the HTTP message across in these cases.
    316     //
    317     if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {
    318       return EFI_INVALID_PARAMETER;
    319     }
    320 
    321     //
    322     // Use existing TCP instance to transmit the packet.
    323     //
    324     Configure   = FALSE;
    325     ReConfigure = FALSE;
    326   } else {
    327     //
    328     // Check whether the token already existed.
    329     //
    330     if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
    331       return EFI_ACCESS_DENIED;
    332     }
    333 
    334     //
    335     // Parse the URI of the remote host.
    336     //
    337     Url = HttpInstance->Url;
    338     UrlLen = StrLen (Request->Url) + 1;
    339     if (UrlLen > HTTP_URL_BUFFER_LEN) {
    340       Url = AllocateZeroPool (UrlLen);
    341       if (Url == NULL) {
    342         return EFI_OUT_OF_RESOURCES;
    343       }
    344       FreePool (HttpInstance->Url);
    345       HttpInstance->Url = Url;
    346     }
    347 
    348 
    349     UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);
    350 
    351     //
    352     // From the information in Url, the HTTP instance will
    353     // be able to determine whether to use http or https.
    354     //
    355     HttpInstance->UseHttps = IsHttpsUrl (Url);
    356 
    357     //
    358     // Check whether we need to create Tls child and open the TLS protocol.
    359     //
    360     if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {
    361       //
    362       // Use TlsSb to create Tls child and open the TLS protocol.
    363       //
    364       HttpInstance->TlsChildHandle = TlsCreateChild (
    365                                        HttpInstance->Service->ImageHandle,
    366                                        &(HttpInstance->Tls),
    367                                        &(HttpInstance->TlsConfiguration)
    368                                        );
    369       if (HttpInstance->TlsChildHandle == NULL) {
    370         return EFI_DEVICE_ERROR;
    371       }
    372 
    373       TlsConfigure = TRUE;
    374     }
    375 
    376     UrlParser = NULL;
    377     Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
    378     if (EFI_ERROR (Status)) {
    379       goto Error1;
    380     }
    381 
    382     HostName   = NULL;
    383     Status     = HttpUrlGetHostName (Url, UrlParser, &HostName);
    384     if (EFI_ERROR (Status)) {
    385      goto Error1;
    386     }
    387 
    388     Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
    389     if (EFI_ERROR (Status)) {
    390       if (HttpInstance->UseHttps) {
    391         RemotePort = HTTPS_DEFAULT_PORT;
    392       } else {
    393         RemotePort = HTTP_DEFAULT_PORT;
    394       }
    395     }
    396     //
    397     // If Configure is TRUE, it indicates the first time to call Request();
    398     // If ReConfigure is TRUE, it indicates the request URL is not same
    399     // with the previous call to Request();
    400     //
    401     Configure   = TRUE;
    402     ReConfigure = TRUE;
    403 
    404     if (HttpInstance->RemoteHost == NULL) {
    405       //
    406       // Request() is called the first time.
    407       //
    408       ReConfigure = FALSE;
    409     } else {
    410       if ((HttpInstance->RemotePort == RemotePort) &&
    411           (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) &&
    412           (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
    413                                        !TlsConfigure &&
    414                                        HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {
    415         //
    416         // Host Name and port number of the request URL are the same with previous call to Request().
    417         // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
    418         // Check whether previous TCP packet sent out.
    419         //
    420 
    421         if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
    422           //
    423           // Wrap the HTTP token in HTTP_TOKEN_WRAP
    424           //
    425           Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
    426           if (Wrap == NULL) {
    427             Status = EFI_OUT_OF_RESOURCES;
    428             goto Error1;
    429           }
    430 
    431           Wrap->HttpToken    = Token;
    432           Wrap->HttpInstance = HttpInstance;
    433 
    434           Status = HttpCreateTcpTxEvent (Wrap);
    435           if (EFI_ERROR (Status)) {
    436             goto Error1;
    437           }
    438 
    439           Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
    440           if (EFI_ERROR (Status)) {
    441             goto Error1;
    442           }
    443 
    444           Wrap->TcpWrap.Method = Request->Method;
    445 
    446           FreePool (HostName);
    447 
    448           //
    449           // Queue the HTTP token and return.
    450           //
    451           return EFI_SUCCESS;
    452         } else {
    453           //
    454           // Use existing TCP instance to transmit the packet.
    455           //
    456           Configure   = FALSE;
    457           ReConfigure = FALSE;
    458         }
    459       } else {
    460         //
    461         // Need close existing TCP instance and create a new TCP instance for data transmit.
    462         //
    463         if (HttpInstance->RemoteHost != NULL) {
    464           FreePool (HttpInstance->RemoteHost);
    465           HttpInstance->RemoteHost = NULL;
    466           HttpInstance->RemotePort = 0;
    467         }
    468       }
    469     }
    470   }
    471 
    472   if (Configure) {
    473     //
    474     // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
    475     //
    476     if (!HttpInstance->LocalAddressIsIPv6) {
    477       Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
    478     } else {
    479       Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
    480     }
    481 
    482     if (EFI_ERROR (Status)) {
    483       HostNameSize = AsciiStrSize (HostName);
    484       HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
    485       if (HostNameStr == NULL) {
    486         Status = EFI_OUT_OF_RESOURCES;
    487         goto Error1;
    488       }
    489 
    490       AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
    491       if (!HttpInstance->LocalAddressIsIPv6) {
    492         Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
    493       } else {
    494         Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);
    495       }
    496 
    497       FreePool (HostNameStr);
    498       if (EFI_ERROR (Status)) {
    499         goto Error1;
    500       }
    501     }
    502 
    503     //
    504     // Save the RemotePort and RemoteHost.
    505     //
    506     ASSERT (HttpInstance->RemoteHost == NULL);
    507     HttpInstance->RemotePort = RemotePort;
    508     HttpInstance->RemoteHost = HostName;
    509     HostName = NULL;
    510   }
    511 
    512   if (ReConfigure) {
    513     //
    514     // The request URL is different from previous calls to Request(), close existing TCP instance.
    515     //
    516     if (!HttpInstance->LocalAddressIsIPv6) {
    517       ASSERT (HttpInstance->Tcp4 != NULL);
    518     } else {
    519       ASSERT (HttpInstance->Tcp6 != NULL);
    520     }
    521 
    522     if (HttpInstance->UseHttps && !TlsConfigure) {
    523       Status = TlsCloseSession (HttpInstance);
    524       if (EFI_ERROR (Status)) {
    525         goto Error1;
    526       }
    527 
    528       TlsCloseTxRxEvent (HttpInstance);
    529     }
    530 
    531     HttpCloseConnection (HttpInstance);
    532     EfiHttpCancel (This, NULL);
    533   }
    534 
    535   //
    536   // Wrap the HTTP token in HTTP_TOKEN_WRAP
    537   //
    538   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
    539   if (Wrap == NULL) {
    540     Status = EFI_OUT_OF_RESOURCES;
    541     goto Error1;
    542   }
    543 
    544   Wrap->HttpToken      = Token;
    545   Wrap->HttpInstance   = HttpInstance;
    546   if (Request != NULL) {
    547     Wrap->TcpWrap.Method = Request->Method;
    548   }
    549 
    550   Status = HttpInitSession (
    551              HttpInstance,
    552              Wrap,
    553              Configure || ReConfigure,
    554              TlsConfigure
    555              );
    556   if (EFI_ERROR (Status)) {
    557     goto Error2;
    558   }
    559 
    560   if (!Configure && !ReConfigure && !TlsConfigure) {
    561     //
    562     // For the new HTTP token, create TX TCP token events.
    563     //
    564     Status = HttpCreateTcpTxEvent (Wrap);
    565     if (EFI_ERROR (Status)) {
    566       goto Error1;
    567     }
    568   }
    569 
    570   //
    571   // Create request message.
    572   //
    573   FileUrl = Url;
    574   if (Url != NULL && *FileUrl != '/') {
    575     //
    576     // Convert the absolute-URI to the absolute-path
    577     //
    578     while (*FileUrl != ':') {
    579       FileUrl++;
    580     }
    581     if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
    582       FileUrl += 3;
    583       while (*FileUrl != '/') {
    584         FileUrl++;
    585       }
    586     } else {
    587       Status = EFI_INVALID_PARAMETER;
    588       goto Error3;
    589     }
    590   }
    591 
    592   Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
    593 
    594   if (EFI_ERROR (Status) || NULL == RequestMsg) {
    595     goto Error3;
    596   }
    597 
    598   ASSERT (RequestMsg != NULL);
    599 
    600   //
    601   // Every request we insert a TxToken and a response call would remove the TxToken.
    602   // In cases of PUT/POST, after an initial request-response pair, we would do a
    603   // continuous request without a response call. So, in such cases, where Request
    604   // structure is NULL, we would not insert a TxToken.
    605   //
    606   if (Request != NULL) {
    607     Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
    608     if (EFI_ERROR (Status)) {
    609       goto Error4;
    610     }
    611   }
    612 
    613   //
    614   // Transmit the request message.
    615   //
    616   Status = HttpTransmitTcp (
    617              HttpInstance,
    618              Wrap,
    619              (UINT8*) RequestMsg,
    620              RequestMsgSize
    621              );
    622   if (EFI_ERROR (Status)) {
    623     goto Error5;
    624   }
    625 
    626   DispatchDpc ();
    627 
    628   if (HostName != NULL) {
    629     FreePool (HostName);
    630   }
    631 
    632   return EFI_SUCCESS;
    633 
    634 Error5:
    635   //
    636   // We would have inserted a TxToken only if Request structure is not NULL.
    637   // Hence check before we do a remove in this error case.
    638   //
    639   if (Request != NULL) {
    640     NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
    641   }
    642 
    643 Error4:
    644   if (RequestMsg != NULL) {
    645     FreePool (RequestMsg);
    646   }
    647 
    648 Error3:
    649   if (HttpInstance->UseHttps) {
    650     TlsCloseSession (HttpInstance);
    651     TlsCloseTxRxEvent (HttpInstance);
    652   }
    653 
    654 Error2:
    655   HttpCloseConnection (HttpInstance);
    656 
    657   HttpCloseTcpConnCloseEvent (HttpInstance);
    658   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
    659     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
    660     Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;
    661   }
    662   if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {
    663     gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
    664     Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;
    665   }
    666 
    667 Error1:
    668   if (HostName != NULL) {
    669     FreePool (HostName);
    670   }
    671   if (Wrap != NULL) {
    672     FreePool (Wrap);
    673   }
    674   if (UrlParser!= NULL) {
    675     HttpUrlFreeParser (UrlParser);
    676   }
    677 
    678   return Status;
    679 
    680 }
    681 
    682 /**
    683   Cancel a user's Token.
    684 
    685   @param[in]  Map                The HTTP instance's token queue.
    686   @param[in]  Item               Object container for one HTTP token and token's wrap.
    687   @param[in]  Context            The user's token to cancel.
    688 
    689   @retval EFI_SUCCESS            Continue to check the next Item.
    690   @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.
    691 
    692 **/
    693 EFI_STATUS
    694 EFIAPI
    695 HttpCancelTokens (
    696   IN NET_MAP                *Map,
    697   IN NET_MAP_ITEM           *Item,
    698   IN VOID                   *Context
    699   )
    700 {
    701   EFI_HTTP_TOKEN            *Token;
    702   HTTP_TOKEN_WRAP           *Wrap;
    703   HTTP_PROTOCOL             *HttpInstance;
    704 
    705   Token = (EFI_HTTP_TOKEN *) Context;
    706 
    707   //
    708   // Return EFI_SUCCESS to check the next item in the map if
    709   // this one doesn't match.
    710   //
    711   if ((Token != NULL) && (Token != Item->Key)) {
    712     return EFI_SUCCESS;
    713   }
    714 
    715   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;
    716   ASSERT (Wrap != NULL);
    717   HttpInstance = Wrap->HttpInstance;
    718 
    719   if (!HttpInstance->LocalAddressIsIPv6) {
    720     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
    721       //
    722       // Cancle the Token before close its Event.
    723       //
    724       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
    725 
    726       //
    727       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
    728       //
    729       DispatchDpc ();
    730     }
    731   } else {
    732     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
    733       //
    734       // Cancle the Token before close its Event.
    735       //
    736       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
    737 
    738       //
    739       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
    740       //
    741       DispatchDpc ();
    742     }
    743   }
    744 
    745   //
    746   // If only one item is to be cancel, return EFI_ABORTED to stop
    747   // iterating the map any more.
    748   //
    749   if (Token != NULL) {
    750     return EFI_ABORTED;
    751   }
    752 
    753   return EFI_SUCCESS;
    754 }
    755 
    756 /**
    757   Cancel the user's receive/transmit request. It is the worker function of
    758   EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
    759   token.
    760 
    761   @param[in]  HttpInstance       Pointer to HTTP_PROTOCOL structure.
    762   @param[in]  Token              The token to cancel. If NULL, all token will be
    763                                  cancelled.
    764 
    765   @retval EFI_SUCCESS            The token is cancelled.
    766   @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.
    767   @retval Others                 Other error as indicated.
    768 
    769 **/
    770 EFI_STATUS
    771 HttpCancel (
    772   IN  HTTP_PROTOCOL             *HttpInstance,
    773   IN  EFI_HTTP_TOKEN            *Token
    774   )
    775 {
    776   EFI_STATUS                    Status;
    777 
    778   //
    779   // First check the tokens queued by EfiHttpRequest().
    780   //
    781   Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);
    782   if (EFI_ERROR (Status)) {
    783     if (Token != NULL) {
    784       if (Status == EFI_ABORTED) {
    785         return EFI_SUCCESS;
    786       }
    787     } else {
    788       return Status;
    789     }
    790   }
    791 
    792   if (!HttpInstance->UseHttps) {
    793     //
    794     // Then check the tokens queued by EfiHttpResponse(), except for Https.
    795     //
    796     Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
    797     if (EFI_ERROR (Status)) {
    798       if (Token != NULL) {
    799         if (Status == EFI_ABORTED) {
    800           return EFI_SUCCESS;
    801         } else {
    802           return EFI_NOT_FOUND;
    803         }
    804       } else {
    805         return Status;
    806       }
    807     }
    808   } else {
    809     if (!HttpInstance->LocalAddressIsIPv6) {
    810       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
    811     } else {
    812       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
    813     }
    814   }
    815 
    816   return EFI_SUCCESS;
    817 }
    818 
    819 
    820 /**
    821   Abort an asynchronous HTTP request or response token.
    822 
    823   The Cancel() function aborts a pending HTTP request or response transaction. If
    824   Token is not NULL and the token is in transmit or receive queues when it is being
    825   cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
    826   be signaled. If the token is not in one of the queues, which usually means that the
    827   asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
    828   all asynchronous tokens issued by Request() or Response() will be aborted.
    829 
    830   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
    831   @param[in]  Token               Point to storage containing HTTP request or response
    832                                   token.
    833 
    834   @retval EFI_SUCCESS             Request and Response queues are successfully flushed.
    835   @retval EFI_INVALID_PARAMETER   This is NULL.
    836   @retval EFI_NOT_STARTED         This instance hasn't been configured.
    837   @retval EFI_NOT_FOUND           The asynchronous request or response token is not
    838                                   found.
    839   @retval EFI_UNSUPPORTED         The implementation does not support this function.
    840 
    841 **/
    842 EFI_STATUS
    843 EFIAPI
    844 EfiHttpCancel (
    845   IN  EFI_HTTP_PROTOCOL         *This,
    846   IN  EFI_HTTP_TOKEN            *Token
    847   )
    848 {
    849   HTTP_PROTOCOL                 *HttpInstance;
    850 
    851   if (This == NULL) {
    852     return EFI_INVALID_PARAMETER;
    853   }
    854 
    855   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    856   ASSERT (HttpInstance != NULL);
    857 
    858   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
    859     return EFI_NOT_STARTED;
    860   }
    861 
    862   return HttpCancel (HttpInstance, Token);
    863 
    864 }
    865 
    866 /**
    867   A callback function to intercept events during message parser.
    868 
    869   This function will be invoked during HttpParseMessageBody() with various events type. An error
    870   return status of the callback function will cause the HttpParseMessageBody() aborted.
    871 
    872   @param[in]    EventType          Event type of this callback call.
    873   @param[in]    Data               A pointer to data buffer.
    874   @param[in]    Length             Length in bytes of the Data.
    875   @param[in]    Context            Callback context set by HttpInitMsgParser().
    876 
    877   @retval EFI_SUCCESS              Continue to parser the message body.
    878 
    879 **/
    880 EFI_STATUS
    881 EFIAPI
    882 HttpBodyParserCallback (
    883   IN HTTP_BODY_PARSE_EVENT      EventType,
    884   IN CHAR8                      *Data,
    885   IN UINTN                      Length,
    886   IN VOID                       *Context
    887   )
    888 {
    889   HTTP_TOKEN_WRAP               *Wrap;
    890   UINTN                         BodyLength;
    891   CHAR8                         *Body;
    892 
    893   if (EventType != BodyParseEventOnComplete) {
    894     return EFI_SUCCESS;
    895   }
    896 
    897   if (Data == NULL || Length != 0 || Context == NULL) {
    898     return EFI_SUCCESS;
    899   }
    900 
    901   Wrap = (HTTP_TOKEN_WRAP *) Context;
    902   Body = Wrap->HttpToken->Message->Body;
    903   BodyLength = Wrap->HttpToken->Message->BodyLength;
    904   if (Data < Body + BodyLength) {
    905     Wrap->HttpInstance->NextMsg = Data;
    906   } else {
    907     Wrap->HttpInstance->NextMsg = NULL;
    908   }
    909 
    910 
    911   //
    912   // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.
    913   //
    914   FreePool (Wrap);
    915 
    916   return EFI_SUCCESS;
    917 }
    918 
    919 /**
    920   The work function of EfiHttpResponse().
    921 
    922   @param[in]  Wrap                Pointer to HTTP token's wrap data.
    923 
    924   @retval EFI_SUCCESS             Allocation succeeded.
    925   @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
    926   @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or
    927                                   the EFI_HTTP_UTILITIES_PROTOCOL is not available.
    928 
    929 **/
    930 EFI_STATUS
    931 HttpResponseWorker (
    932   IN  HTTP_TOKEN_WRAP           *Wrap
    933   )
    934 {
    935   EFI_STATUS                    Status;
    936   EFI_HTTP_MESSAGE              *HttpMsg;
    937   CHAR8                         *EndofHeader;
    938   CHAR8                         *HttpHeaders;
    939   UINTN                         SizeofHeaders;
    940   UINTN                         BufferSize;
    941   UINTN                         StatusCode;
    942   CHAR8                         *Tmp;
    943   CHAR8                         *HeaderTmp;
    944   CHAR8                         *StatusCodeStr;
    945   UINTN                         BodyLen;
    946   HTTP_PROTOCOL                 *HttpInstance;
    947   EFI_HTTP_TOKEN                *Token;
    948   NET_MAP_ITEM                  *Item;
    949   HTTP_TOKEN_WRAP               *ValueInItem;
    950   UINTN                         HdrLen;
    951   NET_FRAGMENT                  Fragment;
    952 
    953   if (Wrap == NULL || Wrap->HttpInstance == NULL) {
    954     return EFI_INVALID_PARAMETER;
    955   }
    956 
    957   HttpInstance = Wrap->HttpInstance;
    958   Token = Wrap->HttpToken;
    959   HttpMsg = Token->Message;
    960 
    961   HttpInstance->EndofHeader = NULL;
    962   HttpInstance->HttpHeaders = NULL;
    963   HttpMsg->Headers          = NULL;
    964   HttpHeaders               = NULL;
    965   SizeofHeaders             = 0;
    966   BufferSize                = 0;
    967   EndofHeader               = NULL;
    968   ValueInItem               = NULL;
    969   Fragment.Len              = 0;
    970   Fragment.Bulk             = NULL;
    971 
    972   if (HttpMsg->Data.Response != NULL) {
    973     //
    974     // Check whether we have cached header from previous call.
    975     //
    976     if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
    977       //
    978       // The data is stored at [NextMsg, CacheBody + CacheLen].
    979       //
    980       HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
    981       HttpHeaders = AllocateZeroPool (HdrLen);
    982       if (HttpHeaders == NULL) {
    983         Status = EFI_OUT_OF_RESOURCES;
    984         goto Error;
    985       }
    986 
    987       CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
    988       FreePool (HttpInstance->CacheBody);
    989       HttpInstance->CacheBody   = NULL;
    990       HttpInstance->NextMsg     = NULL;
    991       HttpInstance->CacheOffset = 0;
    992       SizeofHeaders = HdrLen;
    993       BufferSize = HttpInstance->CacheLen;
    994 
    995       //
    996       // Check whether we cached the whole HTTP headers.
    997       //
    998       EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR);
    999     }
   1000 
   1001     HttpInstance->EndofHeader = &EndofHeader;
   1002     HttpInstance->HttpHeaders = &HttpHeaders;
   1003 
   1004 
   1005     if (HttpInstance->TimeoutEvent == NULL) {
   1006       //
   1007       // Create TimeoutEvent for response
   1008       //
   1009       Status = gBS->CreateEvent (
   1010                       EVT_TIMER,
   1011                       TPL_CALLBACK,
   1012                       NULL,
   1013                       NULL,
   1014                       &HttpInstance->TimeoutEvent
   1015                       );
   1016       if (EFI_ERROR (Status)) {
   1017         goto Error;
   1018       }
   1019     }
   1020 
   1021     //
   1022     // Start the timer, and wait Timeout seconds to receive the header packet.
   1023     //
   1024     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
   1025     if (EFI_ERROR (Status)) {
   1026       goto Error;
   1027     }
   1028 
   1029     Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);
   1030 
   1031     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
   1032 
   1033     if (EFI_ERROR (Status)) {
   1034       goto Error;
   1035     }
   1036 
   1037     ASSERT (HttpHeaders != NULL);
   1038 
   1039     //
   1040     // Cache the part of body.
   1041     //
   1042     BodyLen = BufferSize - (EndofHeader - HttpHeaders);
   1043     if (BodyLen > 0) {
   1044       if (HttpInstance->CacheBody != NULL) {
   1045         FreePool (HttpInstance->CacheBody);
   1046       }
   1047 
   1048       HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
   1049       if (HttpInstance->CacheBody == NULL) {
   1050         Status = EFI_OUT_OF_RESOURCES;
   1051         goto Error;
   1052       }
   1053 
   1054       CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
   1055       HttpInstance->CacheLen = BodyLen;
   1056     }
   1057 
   1058     //
   1059     // Search for Status Code.
   1060     //
   1061     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
   1062     if (StatusCodeStr == NULL) {
   1063       Status = EFI_NOT_READY;
   1064       goto Error;
   1065     }
   1066 
   1067     StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);
   1068 
   1069     //
   1070     // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
   1071     //
   1072     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
   1073     if (Tmp == NULL) {
   1074       Status = EFI_NOT_READY;
   1075       goto Error;
   1076     }
   1077 
   1078     //
   1079     // We could have response with just a HTTP message and no headers. For Example,
   1080     // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
   1081     // method. A "\r\n" following Tmp string again would indicate an end. Compare and
   1082     // set SizeofHeaders to 0.
   1083     //
   1084     Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
   1085     if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
   1086       SizeofHeaders = 0;
   1087     } else {
   1088       SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
   1089     }
   1090 
   1091     HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
   1092     HttpInstance->StatusCode = StatusCode;
   1093 
   1094     Status = EFI_NOT_READY;
   1095     ValueInItem = NULL;
   1096 
   1097     //
   1098     // In cases of PUT/POST, after an initial request-response pair, we would do a
   1099     // continuous request without a response call. So, we would not do an insert of
   1100     // TxToken. After we have sent the complete file, we will call a response to get
   1101     // a final response from server. In such a case, we would not have any TxTokens.
   1102     // Hence, check that case before doing a NetMapRemoveHead.
   1103     //
   1104     if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
   1105       NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
   1106       if (ValueInItem == NULL)  {
   1107         goto Error;
   1108       }
   1109 
   1110       //
   1111       // The first Tx Token not transmitted yet, insert back and return error.
   1112       //
   1113       if (!ValueInItem->TcpWrap.IsTxDone) {
   1114         goto Error2;
   1115       }
   1116     }
   1117 
   1118     if (SizeofHeaders != 0) {
   1119       HeaderTmp = AllocateZeroPool (SizeofHeaders);
   1120       if (HeaderTmp == NULL) {
   1121         Status = EFI_OUT_OF_RESOURCES;
   1122         goto Error2;
   1123       }
   1124 
   1125       CopyMem (HeaderTmp, Tmp, SizeofHeaders);
   1126       FreePool (HttpHeaders);
   1127       HttpHeaders = HeaderTmp;
   1128 
   1129       //
   1130       // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
   1131       //
   1132       if (mHttpUtilities == NULL) {
   1133         Status = EFI_NOT_READY;
   1134         goto Error2;
   1135       }
   1136 
   1137       //
   1138       // Parse the HTTP header into array of key/value pairs.
   1139       //
   1140       Status = mHttpUtilities->Parse (
   1141                                  mHttpUtilities,
   1142                                  HttpHeaders,
   1143                                  SizeofHeaders,
   1144                                  &HttpMsg->Headers,
   1145                                  &HttpMsg->HeaderCount
   1146                                  );
   1147       if (EFI_ERROR (Status)) {
   1148         goto Error2;
   1149       }
   1150 
   1151       FreePool (HttpHeaders);
   1152       HttpHeaders = NULL;
   1153 
   1154 
   1155       //
   1156       // Init message-body parser by header information.
   1157       //
   1158       Status = HttpInitMsgParser (
   1159                  HttpInstance->Method,
   1160                  HttpMsg->Data.Response->StatusCode,
   1161                  HttpMsg->HeaderCount,
   1162                  HttpMsg->Headers,
   1163                  HttpBodyParserCallback,
   1164                  (VOID *) ValueInItem,
   1165                  &HttpInstance->MsgParser
   1166                  );
   1167       if (EFI_ERROR (Status)) {
   1168         goto Error2;
   1169       }
   1170 
   1171       //
   1172       // Check whether we received a complete HTTP message.
   1173       //
   1174       if (HttpInstance->CacheBody != NULL) {
   1175         Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
   1176         if (EFI_ERROR (Status)) {
   1177           goto Error2;
   1178         }
   1179 
   1180         if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
   1181           //
   1182           // Free the MsgParse since we already have a full HTTP message.
   1183           //
   1184           HttpFreeMsgParser (HttpInstance->MsgParser);
   1185           HttpInstance->MsgParser = NULL;
   1186         }
   1187       }
   1188     }
   1189 
   1190     if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
   1191       Status = EFI_SUCCESS;
   1192       goto Exit;
   1193     }
   1194   }
   1195 
   1196   //
   1197   // Receive the response body.
   1198   //
   1199   BodyLen = 0;
   1200 
   1201   //
   1202   // First check whether we cached some data.
   1203   //
   1204   if (HttpInstance->CacheBody != NULL) {
   1205     //
   1206     // Calculate the length of the cached data.
   1207     //
   1208     if (HttpInstance->NextMsg != NULL) {
   1209       //
   1210       // We have a cached HTTP message which includes a part of HTTP header of next message.
   1211       //
   1212       BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);
   1213     } else {
   1214       BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
   1215     }
   1216 
   1217     if (BodyLen > 0) {
   1218       //
   1219       // We have some cached data. Just copy the data and return.
   1220       //
   1221       if (HttpMsg->BodyLength < BodyLen) {
   1222         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
   1223         HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
   1224       } else {
   1225         //
   1226         // Copy all cached data out.
   1227         //
   1228         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
   1229         HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
   1230         HttpMsg->BodyLength = BodyLen;
   1231 
   1232         if (HttpInstance->NextMsg == NULL) {
   1233           //
   1234           // There is no HTTP header of next message. Just free the cache buffer.
   1235           //
   1236           FreePool (HttpInstance->CacheBody);
   1237           HttpInstance->CacheBody   = NULL;
   1238           HttpInstance->NextMsg     = NULL;
   1239           HttpInstance->CacheOffset = 0;
   1240         }
   1241       }
   1242       //
   1243       // Return since we aready received required data.
   1244       //
   1245       Status = EFI_SUCCESS;
   1246       goto Exit;
   1247     }
   1248 
   1249     if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
   1250       //
   1251       // We received a complete HTTP message, and we don't have more data to return to caller.
   1252       //
   1253       HttpMsg->BodyLength = 0;
   1254       Status = EFI_SUCCESS;
   1255       goto Exit;
   1256     }
   1257   }
   1258 
   1259   ASSERT (HttpInstance->MsgParser != NULL);
   1260 
   1261   //
   1262   // We still need receive more data when there is no cache data and MsgParser is not NULL;
   1263   //
   1264   if (!HttpInstance->UseHttps) {
   1265     Status = HttpTcpReceiveBody (Wrap, HttpMsg);
   1266 
   1267     if (EFI_ERROR (Status)) {
   1268       goto Error2;
   1269     }
   1270 
   1271   } else {
   1272     if (HttpInstance->TimeoutEvent == NULL) {
   1273       //
   1274       // Create TimeoutEvent for response
   1275       //
   1276       Status = gBS->CreateEvent (
   1277                       EVT_TIMER,
   1278                       TPL_CALLBACK,
   1279                       NULL,
   1280                       NULL,
   1281                       &HttpInstance->TimeoutEvent
   1282                       );
   1283       if (EFI_ERROR (Status)) {
   1284         goto Error2;
   1285       }
   1286     }
   1287 
   1288     //
   1289     // Start the timer, and wait Timeout seconds to receive the body packet.
   1290     //
   1291     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
   1292     if (EFI_ERROR (Status)) {
   1293       goto Error2;
   1294     }
   1295 
   1296     Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
   1297 
   1298     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
   1299 
   1300     if (EFI_ERROR (Status)) {
   1301       goto Error2;
   1302     }
   1303 
   1304     //
   1305     // Check whether we receive a complete HTTP message.
   1306     //
   1307     Status = HttpParseMessageBody (
   1308                HttpInstance->MsgParser,
   1309                (UINTN) Fragment.Len,
   1310                (CHAR8 *) Fragment.Bulk
   1311                );
   1312     if (EFI_ERROR (Status)) {
   1313       goto Error2;
   1314     }
   1315 
   1316     if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
   1317       //
   1318       // Free the MsgParse since we already have a full HTTP message.
   1319       //
   1320       HttpFreeMsgParser (HttpInstance->MsgParser);
   1321       HttpInstance->MsgParser = NULL;
   1322     }
   1323 
   1324     //
   1325     // We receive part of header of next HTTP msg.
   1326     //
   1327     if (HttpInstance->NextMsg != NULL) {
   1328       HttpMsg->BodyLength = MIN ((UINTN) (HttpInstance->NextMsg - (CHAR8 *) Fragment.Bulk), HttpMsg->BodyLength);
   1329       CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
   1330 
   1331       HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
   1332       if (HttpInstance->CacheLen != 0) {
   1333         if (HttpInstance->CacheBody != NULL) {
   1334           FreePool (HttpInstance->CacheBody);
   1335         }
   1336 
   1337         HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
   1338         if (HttpInstance->CacheBody == NULL) {
   1339           Status = EFI_OUT_OF_RESOURCES;
   1340           goto Error2;
   1341         }
   1342 
   1343         CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
   1344         HttpInstance->CacheOffset = 0;
   1345 
   1346         HttpInstance->NextMsg = HttpInstance->CacheBody + (UINTN) (HttpInstance->NextMsg - (CHAR8 *) (Fragment.Bulk + HttpMsg->BodyLength));
   1347       }
   1348     } else {
   1349       HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);
   1350       CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
   1351       HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
   1352       if (HttpInstance->CacheLen != 0) {
   1353         if (HttpInstance->CacheBody != NULL) {
   1354           FreePool (HttpInstance->CacheBody);
   1355         }
   1356 
   1357         HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
   1358         if (HttpInstance->CacheBody == NULL) {
   1359           Status = EFI_OUT_OF_RESOURCES;
   1360           goto Error2;
   1361         }
   1362 
   1363         CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
   1364         HttpInstance->CacheOffset = 0;
   1365       }
   1366     }
   1367 
   1368     if (Fragment.Bulk != NULL) {
   1369       FreePool (Fragment.Bulk);
   1370       Fragment.Bulk = NULL;
   1371     }
   1372 
   1373     goto Exit;
   1374   }
   1375 
   1376   return Status;
   1377 
   1378 Exit:
   1379   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
   1380   if (Item != NULL) {
   1381     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
   1382   }
   1383 
   1384   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
   1385     Token->Status = EFI_HTTP_ERROR;
   1386   } else {
   1387     Token->Status = Status;
   1388   }
   1389 
   1390   gBS->SignalEvent (Token->Event);
   1391   HttpCloseTcpRxEvent (Wrap);
   1392   FreePool (Wrap);
   1393   return Status;
   1394 
   1395 Error2:
   1396   if (ValueInItem != NULL) {
   1397     NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
   1398   }
   1399 
   1400 Error:
   1401   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
   1402   if (Item != NULL) {
   1403     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
   1404   }
   1405 
   1406   if (!HttpInstance->UseHttps) {
   1407     HttpTcpTokenCleanup (Wrap);
   1408   } else {
   1409     FreePool (Wrap);
   1410   }
   1411 
   1412   if (HttpHeaders != NULL) {
   1413     FreePool (HttpHeaders);
   1414     HttpHeaders = NULL;
   1415   }
   1416 
   1417   if (Fragment.Bulk != NULL) {
   1418     FreePool (Fragment.Bulk);
   1419     Fragment.Bulk = NULL;
   1420   }
   1421 
   1422   if (HttpMsg->Headers != NULL) {
   1423     FreePool (HttpMsg->Headers);
   1424     HttpMsg->Headers = NULL;
   1425   }
   1426 
   1427   if (HttpInstance->CacheBody != NULL) {
   1428     FreePool (HttpInstance->CacheBody);
   1429     HttpInstance->CacheBody = NULL;
   1430   }
   1431 
   1432   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
   1433     Token->Status = EFI_HTTP_ERROR;
   1434   } else {
   1435     Token->Status = Status;
   1436   }
   1437 
   1438   gBS->SignalEvent (Token->Event);
   1439 
   1440   return Status;
   1441 
   1442 }
   1443 
   1444 
   1445 /**
   1446   The Response() function queues an HTTP response to this HTTP instance, similar to
   1447   Receive() function in the EFI TCP driver. When the HTTP response is received successfully,
   1448   or if there is an error, Status in token will be updated and Event will be signaled.
   1449 
   1450   The HTTP driver will queue a receive token to the underlying TCP instance. When data
   1451   is received in the underlying TCP instance, the data will be parsed and Token will
   1452   be populated with the response data. If the data received from the remote host
   1453   contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
   1454   (asynchronously) for more data to be sent from the remote host before signaling
   1455   Event in Token.
   1456 
   1457   It is the responsibility of the caller to allocate a buffer for Body and specify the
   1458   size in BodyLength. If the remote host provides a response that contains a content
   1459   body, up to BodyLength bytes will be copied from the receive buffer into Body and
   1460   BodyLength will be updated with the amount of bytes received and copied to Body. This
   1461   allows the client to download a large file in chunks instead of into one contiguous
   1462   block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
   1463   non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
   1464   token to underlying TCP instance. If data arrives in the receive buffer, up to
   1465   BodyLength bytes of data will be copied to Body. The HTTP driver will then update
   1466   BodyLength with the amount of bytes received and copied to Body.
   1467 
   1468   If the HTTP driver does not have an open underlying TCP connection with the host
   1469   specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
   1470   consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
   1471   an open TCP connection between client and host.
   1472 
   1473   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
   1474   @param[in]  Token               Pointer to storage containing HTTP response token.
   1475 
   1476   @retval EFI_SUCCESS             Allocation succeeded.
   1477   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been
   1478                                   initialized.
   1479   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
   1480                                   This is NULL.
   1481                                   Token is NULL.
   1482                                   Token->Message->Headers is NULL.
   1483                                   Token->Message is NULL.
   1484                                   Token->Message->Body is not NULL,
   1485                                   Token->Message->BodyLength is non-zero, and
   1486                                   Token->Message->Data is NULL, but a previous call to
   1487                                   Response() has not been completed successfully.
   1488   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
   1489   @retval EFI_ACCESS_DENIED       An open TCP connection is not present with the host
   1490                                   specified by response URL.
   1491 **/
   1492 EFI_STATUS
   1493 EFIAPI
   1494 EfiHttpResponse (
   1495   IN  EFI_HTTP_PROTOCOL         *This,
   1496   IN  EFI_HTTP_TOKEN            *Token
   1497   )
   1498 {
   1499   EFI_STATUS                    Status;
   1500   EFI_HTTP_MESSAGE              *HttpMsg;
   1501   HTTP_PROTOCOL                 *HttpInstance;
   1502   HTTP_TOKEN_WRAP               *Wrap;
   1503 
   1504   if ((This == NULL) || (Token == NULL)) {
   1505     return EFI_INVALID_PARAMETER;
   1506   }
   1507 
   1508   HttpMsg = Token->Message;
   1509   if (HttpMsg == NULL) {
   1510     return EFI_INVALID_PARAMETER;
   1511   }
   1512 
   1513   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
   1514   ASSERT (HttpInstance != NULL);
   1515 
   1516   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
   1517     return EFI_NOT_STARTED;
   1518   }
   1519 
   1520   //
   1521   // Check whether the token already existed.
   1522   //
   1523   if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {
   1524     return EFI_ACCESS_DENIED;
   1525   }
   1526 
   1527   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
   1528   if (Wrap == NULL) {
   1529     return EFI_OUT_OF_RESOURCES;
   1530   }
   1531 
   1532   Wrap->HttpInstance = HttpInstance;
   1533   Wrap->HttpToken    = Token;
   1534 
   1535   //
   1536   // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
   1537   // receive the https response. A special TlsRxToken is used for receiving TLS
   1538   // related messages. It should be a blocking response.
   1539   //
   1540   if (!HttpInstance->UseHttps) {
   1541     Status = HttpCreateTcpRxEvent (Wrap);
   1542     if (EFI_ERROR (Status)) {
   1543       goto Error;
   1544     }
   1545   }
   1546 
   1547   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);
   1548   if (EFI_ERROR (Status)) {
   1549     goto Error;
   1550   }
   1551 
   1552   //
   1553   // If already have pending RxTokens, return directly.
   1554   //
   1555   if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {
   1556     return EFI_SUCCESS;
   1557   }
   1558 
   1559   return HttpResponseWorker (Wrap);
   1560 
   1561 Error:
   1562   if (Wrap != NULL) {
   1563     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
   1564       gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
   1565     }
   1566 
   1567     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
   1568       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
   1569     }
   1570     FreePool (Wrap);
   1571   }
   1572 
   1573   return Status;
   1574 }
   1575 
   1576 /**
   1577   The Poll() function can be used by network drivers and applications to increase the
   1578   rate that data packets are moved between the communication devices and the transmit
   1579   and receive queues.
   1580 
   1581   In some systems, the periodic timer event in the managed network driver may not poll
   1582   the underlying communications device fast enough to transmit and/or receive all data
   1583   packets without missing incoming packets or dropping outgoing packets. Drivers and
   1584   applications that are experiencing packet loss should try calling the Poll() function
   1585   more often.
   1586 
   1587   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
   1588 
   1589   @retval EFI_SUCCESS             Incoming or outgoing data was processed.
   1590   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
   1591   @retval EFI_INVALID_PARAMETER   This is NULL.
   1592   @retval EFI_NOT_READY           No incoming or outgoing data is processed.
   1593   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
   1594 
   1595 **/
   1596 EFI_STATUS
   1597 EFIAPI
   1598 EfiHttpPoll (
   1599   IN  EFI_HTTP_PROTOCOL         *This
   1600   )
   1601 {
   1602   EFI_STATUS                    Status;
   1603   HTTP_PROTOCOL                 *HttpInstance;
   1604 
   1605   if (This == NULL) {
   1606     return EFI_INVALID_PARAMETER;
   1607   }
   1608 
   1609   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
   1610   ASSERT (HttpInstance != NULL);
   1611 
   1612   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
   1613     return EFI_NOT_STARTED;
   1614   }
   1615 
   1616   if (HttpInstance->LocalAddressIsIPv6) {
   1617     if (HttpInstance->Tcp6 == NULL) {
   1618       return EFI_NOT_STARTED;
   1619     }
   1620     Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
   1621   } else {
   1622     if (HttpInstance->Tcp4 == NULL) {
   1623       return EFI_NOT_STARTED;
   1624     }
   1625     Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
   1626   }
   1627 
   1628   DispatchDpc ();
   1629 
   1630   return Status;
   1631 }
   1632