Home | History | Annotate | Download | only in HttpDxe
      1 /** @file
      2   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
      3 
      4   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
      5   (C) Copyright 2015 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                                   HttpConfigData->AccessPoint is NULL.
     43   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
     44   @retval EFI_NOT_STARTED         The HTTP instance is not configured.
     45 
     46 **/
     47 EFI_STATUS
     48 EFIAPI
     49 EfiHttpGetModeData (
     50   IN  EFI_HTTP_PROTOCOL         *This,
     51   OUT EFI_HTTP_CONFIG_DATA      *HttpConfigData
     52   )
     53 {
     54   HTTP_PROTOCOL                 *HttpInstance;
     55   EFI_HTTPv4_ACCESS_POINT       *Http4AccessPoint;
     56   EFI_HTTPv6_ACCESS_POINT       *Http6AccessPoint;
     57 
     58   if ((This == NULL) || (HttpConfigData == NULL)) {
     59     return EFI_INVALID_PARAMETER;
     60   }
     61 
     62   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
     63   ASSERT (HttpInstance != NULL);
     64 
     65   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
     66     return EFI_NOT_STARTED;
     67   }
     68 
     69   HttpConfigData->HttpVersion        = HttpInstance->HttpVersion;
     70   HttpConfigData->TimeOutMillisec    = HttpInstance->TimeOutMillisec;
     71   HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;
     72 
     73   if (HttpInstance->LocalAddressIsIPv6) {
     74     Http6AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv6_ACCESS_POINT));
     75     if (Http6AccessPoint == NULL) {
     76       return EFI_OUT_OF_RESOURCES;
     77     }
     78     CopyMem (
     79       Http6AccessPoint,
     80       &HttpInstance->Ipv6Node,
     81       sizeof (HttpInstance->Ipv6Node)
     82     );
     83     HttpConfigData->AccessPoint.IPv6Node = Http6AccessPoint;
     84   } else {
     85     Http4AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv4_ACCESS_POINT));
     86     if (Http4AccessPoint == NULL) {
     87       return EFI_OUT_OF_RESOURCES;
     88     }
     89     CopyMem (
     90       Http4AccessPoint,
     91       &HttpInstance->IPv4Node,
     92       sizeof (HttpInstance->IPv4Node)
     93       );
     94     HttpConfigData->AccessPoint.IPv4Node = Http4AccessPoint;
     95   }
     96 
     97   return EFI_SUCCESS;
     98 }
     99 
    100 /**
    101   Initialize or brutally reset the operational parameters for this EFI HTTP instance.
    102 
    103   The Configure() function does the following:
    104   When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring
    105   timeout, local address, port, etc.
    106   When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active
    107   connections with remote hosts, canceling all asynchronous tokens, and flush request
    108   and response buffers without informing the appropriate hosts.
    109 
    110   Except for GetModeData() and Configure(), No other EFI HTTP function can be executed
    111   by this instance until the Configure() function is executed and returns successfully.
    112 
    113   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
    114   @param[in]  HttpConfigData      Pointer to the configure data to configure the instance.
    115 
    116   @retval EFI_SUCCESS             Operation succeeded.
    117   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
    118                                   This is NULL.
    119                                   HttpConfigData->LocalAddressIsIPv6 is FALSE and
    120                                   HttpConfigData->IPv4Node is NULL.
    121                                   HttpConfigData->LocalAddressIsIPv6 is TRUE and
    122                                   HttpConfigData->IPv6Node is NULL.
    123   @retval EFI_ALREADY_STARTED     Reinitialize this HTTP instance without calling
    124                                   Configure() with NULL to reset it.
    125   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
    126   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources when
    127                                   executing Configure().
    128   @retval EFI_UNSUPPORTED         One or more options in HttpConfigData are not supported
    129                                   in the implementation.
    130 **/
    131 EFI_STATUS
    132 EFIAPI
    133 EfiHttpConfigure (
    134   IN  EFI_HTTP_PROTOCOL         *This,
    135   IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData
    136   )
    137 {
    138   HTTP_PROTOCOL                 *HttpInstance;
    139   EFI_STATUS                    Status;
    140 
    141   //
    142   // Check input parameters.
    143   //
    144   if (This == NULL ||
    145      (HttpConfigData != NULL && ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
    146                                  (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {
    147     return EFI_INVALID_PARAMETER;
    148   }
    149 
    150   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    151   ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);
    152 
    153   if (HttpConfigData != NULL) {
    154 
    155     //
    156     // Now configure this HTTP instance.
    157     //
    158     if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {
    159       return EFI_ALREADY_STARTED;
    160     }
    161 
    162     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;
    163     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;
    164     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
    165 
    166     if (HttpConfigData->LocalAddressIsIPv6) {
    167       CopyMem (
    168         &HttpInstance->Ipv6Node,
    169         HttpConfigData->AccessPoint.IPv6Node,
    170         sizeof (HttpInstance->Ipv6Node)
    171         );
    172     } else {
    173       CopyMem (
    174         &HttpInstance->IPv4Node,
    175         HttpConfigData->AccessPoint.IPv4Node,
    176         sizeof (HttpInstance->IPv4Node)
    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->Message is NULL.
    221                                   Token->Message->Body is not NULL,
    222                                   Token->Message->BodyLength is non-zero, and
    223                                   Token->Message->Data is NULL, but a previous call to
    224                                   Request()has not been completed successfully.
    225 **/
    226 EFI_STATUS
    227 EFIAPI
    228 EfiHttpRequest (
    229   IN  EFI_HTTP_PROTOCOL         *This,
    230   IN  EFI_HTTP_TOKEN            *Token
    231   )
    232 {
    233   EFI_HTTP_MESSAGE              *HttpMsg;
    234   EFI_HTTP_REQUEST_DATA         *Request;
    235   VOID                          *UrlParser;
    236   EFI_STATUS                    Status;
    237   CHAR8                         *HostName;
    238   UINT16                        RemotePort;
    239   HTTP_PROTOCOL                 *HttpInstance;
    240   BOOLEAN                       Configure;
    241   BOOLEAN                       ReConfigure;
    242   CHAR8                         *RequestStr;
    243   CHAR8                         *Url;
    244   UINTN                         UrlLen;
    245   CHAR16                        *HostNameStr;
    246   HTTP_TOKEN_WRAP               *Wrap;
    247   CHAR8                         *FileUrl;
    248 
    249   if ((This == NULL) || (Token == NULL)) {
    250     return EFI_INVALID_PARAMETER;
    251   }
    252 
    253   HttpMsg = Token->Message;
    254   if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) {
    255     return EFI_INVALID_PARAMETER;
    256   }
    257 
    258   //
    259   // Current implementation does not support POST/PUT method.
    260   // If future version supports these two methods, Request could be NULL for a special case that to send large amounts
    261   // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.
    262   //
    263   //
    264   Request = HttpMsg->Data.Request;
    265   if ((Request == NULL) || (Request->Url == NULL)) {
    266     return EFI_INVALID_PARAMETER;
    267   }
    268 
    269   //
    270   // Only support GET and HEAD method in current implementation.
    271   //
    272   if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) {
    273     return EFI_UNSUPPORTED;
    274   }
    275 
    276   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    277   ASSERT (HttpInstance != NULL);
    278 
    279   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
    280     return EFI_NOT_STARTED;
    281   }
    282 
    283   //
    284   // Check whether the token already existed.
    285   //
    286   if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
    287     return EFI_ACCESS_DENIED;
    288   }
    289 
    290   HostName    = NULL;
    291   Wrap        = NULL;
    292   HostNameStr = NULL;
    293 
    294   //
    295   // Parse the URI of the remote host.
    296   //
    297   Url = HttpInstance->Url;
    298   UrlLen = StrLen (Request->Url) + 1;
    299   if (UrlLen > HTTP_URL_BUFFER_LEN) {
    300     Url = AllocateZeroPool (UrlLen);
    301     if (Url == NULL) {
    302       return EFI_OUT_OF_RESOURCES;
    303     }
    304     FreePool (HttpInstance->Url);
    305     HttpInstance->Url = Url;
    306   }
    307 
    308 
    309   UnicodeStrToAsciiStr (Request->Url, Url);
    310   UrlParser = NULL;
    311   Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
    312   if (EFI_ERROR (Status)) {
    313     goto Error1;
    314   }
    315 
    316   RequestStr = NULL;
    317   HostName   = NULL;
    318   Status     = HttpUrlGetHostName (Url, UrlParser, &HostName);
    319   if (EFI_ERROR (Status)) {
    320     goto Error1;
    321   }
    322 
    323   Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
    324   if (EFI_ERROR (Status)) {
    325     RemotePort = HTTP_DEFAULT_PORT;
    326   }
    327   //
    328   // If Configure is TRUE, it indicates the first time to call Request();
    329   // If ReConfigure is TRUE, it indicates the request URL is not same
    330   // with the previous call to Request();
    331   //
    332   Configure   = TRUE;
    333   ReConfigure = TRUE;
    334 
    335   if (HttpInstance->RemoteHost == NULL) {
    336     //
    337     // Request() is called the first time.
    338     //
    339     ReConfigure = FALSE;
    340   } else {
    341     if ((HttpInstance->RemotePort == RemotePort) &&
    342         (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {
    343       //
    344       // Host Name and port number of the request URL are the same with previous call to Request().
    345       // Check whether previous TCP packet sent out.
    346       //
    347       if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
    348         //
    349         // Wrap the HTTP token in HTTP_TOKEN_WRAP
    350         //
    351         Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
    352         if (Wrap == NULL) {
    353           Status = EFI_OUT_OF_RESOURCES;
    354           goto Error1;
    355         }
    356 
    357         Wrap->HttpToken    = Token;
    358         Wrap->HttpInstance = HttpInstance;
    359 
    360         Status = HttpCreateTcpTxEvent (Wrap);
    361         if (EFI_ERROR (Status)) {
    362           goto Error1;
    363         }
    364 
    365         Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
    366         if (EFI_ERROR (Status)) {
    367           goto Error1;
    368         }
    369 
    370         Wrap->TcpWrap.Method = Request->Method;
    371 
    372         FreePool (HostName);
    373 
    374         //
    375         // Queue the HTTP token and return.
    376         //
    377         return EFI_SUCCESS;
    378       } else {
    379         //
    380         // Use existing TCP instance to transmit the packet.
    381         //
    382         Configure   = FALSE;
    383         ReConfigure = FALSE;
    384       }
    385     } else {
    386       //
    387       // Need close existing TCP instance and create a new TCP instance for data transmit.
    388       //
    389       if (HttpInstance->RemoteHost != NULL) {
    390         FreePool (HttpInstance->RemoteHost);
    391         HttpInstance->RemoteHost = NULL;
    392         HttpInstance->RemotePort = 0;
    393       }
    394     }
    395   }
    396 
    397   if (Configure) {
    398     //
    399     // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
    400     //
    401     if (!HttpInstance->LocalAddressIsIPv6) {
    402       Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
    403     } else {
    404       Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
    405     }
    406 
    407     if (EFI_ERROR (Status)) {
    408       HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));
    409       if (HostNameStr == NULL) {
    410         Status = EFI_OUT_OF_RESOURCES;
    411         goto Error1;
    412       }
    413 
    414       AsciiStrToUnicodeStr (HostName, HostNameStr);
    415       if (!HttpInstance->LocalAddressIsIPv6) {
    416         Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
    417       } else {
    418         Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);
    419       }
    420 
    421       FreePool (HostNameStr);
    422       if (EFI_ERROR (Status)) {
    423         goto Error1;
    424       }
    425     }
    426 
    427 
    428     //
    429     // Save the RemotePort and RemoteHost.
    430     //
    431     ASSERT (HttpInstance->RemoteHost == NULL);
    432     HttpInstance->RemotePort = RemotePort;
    433     HttpInstance->RemoteHost = HostName;
    434     HostName = NULL;
    435   }
    436 
    437   if (ReConfigure) {
    438     //
    439     // The request URL is different from previous calls to Request(), close existing TCP instance.
    440     //
    441     if (!HttpInstance->LocalAddressIsIPv6) {
    442       ASSERT (HttpInstance->Tcp4 != NULL);
    443     } else {
    444       ASSERT (HttpInstance->Tcp6 != NULL);
    445     }
    446     HttpCloseConnection (HttpInstance);
    447     EfiHttpCancel (This, NULL);
    448   }
    449 
    450   //
    451   // Wrap the HTTP token in HTTP_TOKEN_WRAP
    452   //
    453   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
    454   if (Wrap == NULL) {
    455     Status = EFI_OUT_OF_RESOURCES;
    456     goto Error1;
    457   }
    458 
    459   Wrap->HttpToken      = Token;
    460   Wrap->HttpInstance   = HttpInstance;
    461   Wrap->TcpWrap.Method = Request->Method;
    462 
    463   Status = HttpInitTcp (HttpInstance, Wrap, Configure);
    464   if (EFI_ERROR (Status)) {
    465     goto Error2;
    466   }
    467 
    468   if (!Configure) {
    469     //
    470     // For the new HTTP token, create TX TCP token events.
    471     //
    472     Status = HttpCreateTcpTxEvent (Wrap);
    473     if (EFI_ERROR (Status)) {
    474       goto Error1;
    475     }
    476   }
    477 
    478   //
    479   // Create request message.
    480   //
    481   FileUrl = Url;
    482   if (*FileUrl != '/') {
    483     //
    484     // Convert the absolute-URI to the absolute-path
    485     //
    486     while (*FileUrl != ':') {
    487       FileUrl++;
    488     }
    489     if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
    490       FileUrl += 3;
    491       while (*FileUrl != '/') {
    492         FileUrl++;
    493       }
    494     } else {
    495       Status = EFI_INVALID_PARAMETER;
    496       goto Error3;
    497     }
    498   }
    499   RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, FileUrl);
    500   if (RequestStr == NULL) {
    501     Status = EFI_OUT_OF_RESOURCES;
    502     goto Error3;
    503   }
    504 
    505   Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
    506   if (EFI_ERROR (Status)) {
    507     goto Error4;
    508   }
    509 
    510   //
    511   // Transmit the request message.
    512   //
    513   Status = HttpTransmitTcp (
    514              HttpInstance,
    515              Wrap,
    516              (UINT8*) RequestStr,
    517              AsciiStrLen (RequestStr)
    518              );
    519   if (EFI_ERROR (Status)) {
    520     goto Error5;
    521   }
    522 
    523   DispatchDpc ();
    524 
    525   if (HostName != NULL) {
    526     FreePool (HostName);
    527   }
    528 
    529   return EFI_SUCCESS;
    530 
    531 Error5:
    532     NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
    533 
    534 Error4:
    535   if (RequestStr != NULL) {
    536     FreePool (RequestStr);
    537   }
    538 
    539 Error3:
    540   HttpCloseConnection (HttpInstance);
    541 
    542 Error2:
    543   HttpCloseTcpConnCloseEvent (HttpInstance);
    544   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
    545     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
    546     Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;
    547   }
    548   if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {
    549     gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
    550     Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;
    551   }
    552 
    553 Error1:
    554 
    555   if (HostName != NULL) {
    556     FreePool (HostName);
    557   }
    558   if (Wrap != NULL) {
    559     FreePool (Wrap);
    560   }
    561   if (UrlParser!= NULL) {
    562     HttpUrlFreeParser (UrlParser);
    563   }
    564 
    565   return Status;
    566 
    567 }
    568 
    569 /**
    570   Cancel a user's Token.
    571 
    572   @param[in]  Map                The HTTP instance's token queue.
    573   @param[in]  Item               Object container for one HTTP token and token's wrap.
    574   @param[in]  Context            The user's token to cancel.
    575 
    576   @retval EFI_SUCCESS            Continue to check the next Item.
    577   @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.
    578 
    579 **/
    580 EFI_STATUS
    581 EFIAPI
    582 HttpCancelTokens (
    583   IN NET_MAP                *Map,
    584   IN NET_MAP_ITEM           *Item,
    585   IN VOID                   *Context
    586   )
    587 {
    588 
    589   EFI_HTTP_TOKEN            *Token;
    590   HTTP_TOKEN_WRAP           *Wrap;
    591   HTTP_PROTOCOL             *HttpInstance;
    592 
    593   Token = (EFI_HTTP_TOKEN *) Context;
    594 
    595   //
    596   // Return EFI_SUCCESS to check the next item in the map if
    597   // this one doesn't match.
    598   //
    599   if ((Token != NULL) && (Token != Item->Key)) {
    600     return EFI_SUCCESS;
    601   }
    602 
    603   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;
    604   ASSERT (Wrap != NULL);
    605   HttpInstance = Wrap->HttpInstance;
    606 
    607   //
    608   // Free resources.
    609   //
    610   NetMapRemoveItem (Map, Item, NULL);
    611 
    612   if (!HttpInstance->LocalAddressIsIPv6) {
    613     if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) {
    614       gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
    615     }
    616 
    617     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
    618       gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
    619     }
    620 
    621     if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
    622       FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer);
    623     }
    624 
    625   } else {
    626     if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) {
    627       gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
    628     }
    629 
    630     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
    631       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
    632     }
    633 
    634     if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
    635       FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer);
    636     }
    637   }
    638 
    639 
    640   FreePool (Wrap);
    641 
    642   //
    643   // If only one item is to be cancel, return EFI_ABORTED to stop
    644   // iterating the map any more.
    645   //
    646   if (Token != NULL) {
    647     return EFI_ABORTED;
    648   }
    649 
    650   return EFI_SUCCESS;
    651 }
    652 
    653 /**
    654   Cancel the user's receive/transmit request. It is the worker function of
    655   EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
    656   token.
    657 
    658   @param[in]  HttpInstance       Pointer to HTTP_PROTOCOL structure.
    659   @param[in]  Token              The token to cancel. If NULL, all token will be
    660                                  cancelled.
    661 
    662   @retval EFI_SUCCESS            The token is cancelled.
    663   @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.
    664   @retval Others                 Other error as indicated.
    665 
    666 **/
    667 EFI_STATUS
    668 HttpCancel (
    669   IN  HTTP_PROTOCOL             *HttpInstance,
    670   IN  EFI_HTTP_TOKEN            *Token
    671   )
    672 {
    673   EFI_STATUS                    Status;
    674 
    675   //
    676   // First check the tokens queued by EfiHttpRequest().
    677   //
    678   Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);
    679   if (EFI_ERROR (Status)) {
    680     if (Token != NULL) {
    681       if (Status == EFI_ABORTED) {
    682         return EFI_SUCCESS;
    683       }
    684     } else {
    685       return Status;
    686     }
    687   }
    688 
    689   //
    690   // Then check the tokens queued by EfiHttpResponse().
    691   //
    692   Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
    693   if (EFI_ERROR (Status)) {
    694     if (Token != NULL) {
    695       if (Status == EFI_ABORTED) {
    696         return EFI_SUCCESS;
    697       } else {
    698         return EFI_NOT_FOUND;
    699       }
    700     } else {
    701       return Status;
    702     }
    703   }
    704 
    705   return EFI_SUCCESS;
    706 }
    707 
    708 
    709 /**
    710   Abort an asynchronous HTTP request or response token.
    711 
    712   The Cancel() function aborts a pending HTTP request or response transaction. If
    713   Token is not NULL and the token is in transmit or receive queues when it is being
    714   cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
    715   be signaled. If the token is not in one of the queues, which usually means that the
    716   asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
    717   all asynchronous tokens issued by Request() or Response() will be aborted.
    718 
    719   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
    720   @param[in]  Token               Point to storage containing HTTP request or response
    721                                   token.
    722 
    723   @retval EFI_SUCCESS             Request and Response queues are successfully flushed.
    724   @retval EFI_INVALID_PARAMETER   This is NULL.
    725   @retval EFI_NOT_STARTED         This instance hasn't been configured.
    726   @retval EFI_NO_MAPPING          When using the default address, configuration (DHCP,
    727                                   BOOTP, RARP, etc.) hasn't finished yet.
    728   @retval EFI_NOT_FOUND           The asynchronous request or response token is not
    729                                   found.
    730   @retval EFI_UNSUPPORTED         The implementation does not support this function.
    731 
    732 **/
    733 EFI_STATUS
    734 EFIAPI
    735 EfiHttpCancel (
    736   IN  EFI_HTTP_PROTOCOL         *This,
    737   IN  EFI_HTTP_TOKEN            *Token
    738   )
    739 {
    740   HTTP_PROTOCOL                 *HttpInstance;
    741 
    742   if (This == NULL) {
    743     return EFI_INVALID_PARAMETER;
    744   }
    745 
    746   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
    747   ASSERT (HttpInstance != NULL);
    748 
    749   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
    750     return EFI_NOT_STARTED;
    751   }
    752 
    753   return HttpCancel (HttpInstance, Token);
    754 
    755 }
    756 
    757 /**
    758   A callback function to intercept events during message parser.
    759 
    760   This function will be invoked during HttpParseMessageBody() with various events type. An error
    761   return status of the callback function will cause the HttpParseMessageBody() aborted.
    762 
    763   @param[in]    EventType          Event type of this callback call.
    764   @param[in]    Data               A pointer to data buffer.
    765   @param[in]    Length             Length in bytes of the Data.
    766   @param[in]    Context            Callback context set by HttpInitMsgParser().
    767 
    768   @retval EFI_SUCCESS              Continue to parser the message body.
    769 
    770 **/
    771 EFI_STATUS
    772 EFIAPI
    773 HttpBodyParserCallback (
    774   IN HTTP_BODY_PARSE_EVENT      EventType,
    775   IN CHAR8                      *Data,
    776   IN UINTN                      Length,
    777   IN VOID                       *Context
    778   )
    779 {
    780   HTTP_TOKEN_WRAP               *Wrap;
    781   UINTN                         BodyLength;
    782   CHAR8                         *Body;
    783 
    784   if (EventType != BodyParseEventOnComplete) {
    785     return EFI_SUCCESS;
    786   }
    787 
    788   if (Data == NULL || Length != 0 || Context == NULL) {
    789     return EFI_SUCCESS;
    790   }
    791 
    792   Wrap = (HTTP_TOKEN_WRAP *) Context;
    793   Body = Wrap->HttpToken->Message->Body;
    794   BodyLength = Wrap->HttpToken->Message->BodyLength;
    795   if (Data < Body + BodyLength) {
    796     Wrap->HttpInstance->NextMsg = Data;
    797   } else {
    798     Wrap->HttpInstance->NextMsg = NULL;
    799   }
    800 
    801 
    802   //
    803   // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.
    804   //
    805   FreePool (Wrap);
    806 
    807   return EFI_SUCCESS;
    808 }
    809 
    810 /**
    811   The work function of EfiHttpResponse().
    812 
    813   @param[in]  Wrap                Pointer to HTTP token's wrap data.
    814 
    815   @retval EFI_SUCCESS             Allocation succeeded.
    816   @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
    817   @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or
    818                                   the EFI_HTTP_UTILITIES_PROTOCOL is not available.
    819 
    820 **/
    821 EFI_STATUS
    822 HttpResponseWorker (
    823   IN  HTTP_TOKEN_WRAP           *Wrap
    824   )
    825 {
    826   EFI_STATUS                    Status;
    827   EFI_HTTP_MESSAGE              *HttpMsg;
    828   CHAR8                         *EndofHeader;
    829   CHAR8                         *HttpHeaders;
    830   UINTN                         SizeofHeaders;
    831   UINTN                         BufferSize;
    832   UINTN                         StatusCode;
    833   CHAR8                         *Tmp;
    834   CHAR8                         *HeaderTmp;
    835   CHAR8                         *StatusCodeStr;
    836   UINTN                         BodyLen;
    837   HTTP_PROTOCOL                 *HttpInstance;
    838   EFI_HTTP_TOKEN                *Token;
    839   NET_MAP_ITEM                  *Item;
    840   HTTP_TOKEN_WRAP               *ValueInItem;
    841   UINTN                         HdrLen;
    842 
    843   if (Wrap == NULL || Wrap->HttpInstance == NULL) {
    844     return EFI_INVALID_PARAMETER;
    845   }
    846 
    847   HttpInstance = Wrap->HttpInstance;
    848   Token = Wrap->HttpToken;
    849   HttpMsg = Token->Message;
    850 
    851   HttpInstance->EndofHeader = NULL;
    852   HttpInstance->HttpHeaders = NULL;
    853   HttpMsg->Headers          = NULL;
    854   HttpHeaders               = NULL;
    855   SizeofHeaders             = 0;
    856   BufferSize                = 0;
    857   EndofHeader               = NULL;
    858 
    859   if (HttpMsg->Data.Response != NULL) {
    860     //
    861     // Need receive the HTTP headers, prepare buffer.
    862     //
    863     Status = HttpCreateTcpRxEventForHeader (HttpInstance);
    864     if (EFI_ERROR (Status)) {
    865       goto Error;
    866     }
    867 
    868     //
    869     // Check whether we have cached header from previous call.
    870     //
    871     if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
    872       //
    873       // The data is stored at [NextMsg, CacheBody + CacheLen].
    874       //
    875       HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
    876       HttpHeaders = AllocateZeroPool (HdrLen);
    877       if (HttpHeaders == NULL) {
    878         Status = EFI_OUT_OF_RESOURCES;
    879         goto Error;
    880       }
    881 
    882       CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
    883       FreePool (HttpInstance->CacheBody);
    884       HttpInstance->CacheBody   = NULL;
    885       HttpInstance->NextMsg     = NULL;
    886       HttpInstance->CacheOffset = 0;
    887       SizeofHeaders = HdrLen;
    888       BufferSize = HttpInstance->CacheLen;
    889 
    890       //
    891       // Check whether we cached the whole HTTP headers.
    892       //
    893       EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR);
    894     }
    895 
    896     HttpInstance->EndofHeader = &EndofHeader;
    897     HttpInstance->HttpHeaders = &HttpHeaders;
    898 
    899     Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize);
    900     if (EFI_ERROR (Status)) {
    901       goto Error;
    902     }
    903 
    904     ASSERT (HttpHeaders != NULL);
    905 
    906     //
    907     // Cache the part of body.
    908     //
    909     BodyLen = BufferSize - (EndofHeader - HttpHeaders);
    910     if (BodyLen > 0) {
    911       if (HttpInstance->CacheBody != NULL) {
    912         FreePool (HttpInstance->CacheBody);
    913       }
    914 
    915       HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
    916       if (HttpInstance->CacheBody == NULL) {
    917         Status = EFI_OUT_OF_RESOURCES;
    918         goto Error;
    919       }
    920 
    921       CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
    922       HttpInstance->CacheLen = BodyLen;
    923     }
    924 
    925     //
    926     // Search for Status Code.
    927     //
    928     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
    929     if (StatusCodeStr == NULL) {
    930       goto Error;
    931     }
    932 
    933     StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);
    934 
    935     //
    936     // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
    937     //
    938     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
    939     if (Tmp == NULL) {
    940       goto Error;
    941     }
    942 
    943     Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
    944     SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
    945     HeaderTmp = AllocateZeroPool (SizeofHeaders);
    946     if (HeaderTmp == NULL) {
    947       goto Error;
    948     }
    949 
    950     CopyMem (HeaderTmp, Tmp, SizeofHeaders);
    951     FreePool (HttpHeaders);
    952     HttpHeaders = HeaderTmp;
    953 
    954     //
    955     // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
    956     //
    957     if (mHttpUtilities == NULL) {
    958       Status = EFI_NOT_READY;
    959       goto Error;
    960     }
    961 
    962     //
    963     // Parse the HTTP header into array of key/value pairs.
    964     //
    965     Status = mHttpUtilities->Parse (
    966                                mHttpUtilities,
    967                                HttpHeaders,
    968                                SizeofHeaders,
    969                                &HttpMsg->Headers,
    970                                &HttpMsg->HeaderCount
    971                                );
    972     if (EFI_ERROR (Status)) {
    973       goto Error;
    974     }
    975 
    976     FreePool (HttpHeaders);
    977     HttpHeaders = NULL;
    978 
    979     HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
    980 
    981     //
    982     // Init message-body parser by header information.
    983     //
    984     Status = EFI_NOT_READY;
    985     ValueInItem = NULL;
    986     NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
    987     if (ValueInItem == NULL)  {
    988       goto Error;
    989     }
    990 
    991     //
    992     // The first Tx Token not transmitted yet, insert back and return error.
    993     //
    994     if (!ValueInItem->TcpWrap.IsTxDone) {
    995       goto Error2;
    996     }
    997 
    998     Status = HttpInitMsgParser (
    999                ValueInItem->TcpWrap.Method,
   1000                HttpMsg->Data.Response->StatusCode,
   1001                HttpMsg->HeaderCount,
   1002                HttpMsg->Headers,
   1003                HttpBodyParserCallback,
   1004                (VOID *) ValueInItem,
   1005                &HttpInstance->MsgParser
   1006                );
   1007     if (EFI_ERROR (Status)) {
   1008       goto Error2;
   1009     }
   1010 
   1011     //
   1012     // Check whether we received a complete HTTP message.
   1013     //
   1014     if (HttpInstance->CacheBody != NULL) {
   1015       Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
   1016       if (EFI_ERROR (Status)) {
   1017         goto Error2;
   1018       }
   1019 
   1020       if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
   1021         //
   1022         // Free the MsgParse since we already have a full HTTP message.
   1023         //
   1024         HttpFreeMsgParser (HttpInstance->MsgParser);
   1025         HttpInstance->MsgParser = NULL;
   1026       }
   1027     }
   1028 
   1029     if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
   1030       Status = EFI_SUCCESS;
   1031       goto Exit;
   1032     }
   1033   }
   1034 
   1035   //
   1036   // Receive the response body.
   1037   //
   1038   BodyLen = 0;
   1039 
   1040   //
   1041   // First check whether we cached some data.
   1042   //
   1043   if (HttpInstance->CacheBody != NULL) {
   1044     //
   1045     // Calculate the length of the cached data.
   1046     //
   1047     if (HttpInstance->NextMsg != NULL) {
   1048       //
   1049       // We have a cached HTTP message which includes a part of HTTP header of next message.
   1050       //
   1051       BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);
   1052     } else {
   1053       BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
   1054     }
   1055 
   1056     if (BodyLen > 0) {
   1057       //
   1058       // We have some cached data. Just copy the data and return.
   1059       //
   1060       if (HttpMsg->BodyLength < BodyLen) {
   1061         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
   1062         HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
   1063       } else {
   1064         //
   1065         // Copy all cached data out.
   1066         //
   1067         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
   1068         HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
   1069         HttpMsg->BodyLength = BodyLen;
   1070 
   1071         if (HttpInstance->NextMsg == NULL) {
   1072           //
   1073           // There is no HTTP header of next message. Just free the cache buffer.
   1074           //
   1075           FreePool (HttpInstance->CacheBody);
   1076           HttpInstance->CacheBody   = NULL;
   1077           HttpInstance->NextMsg     = NULL;
   1078           HttpInstance->CacheOffset = 0;
   1079         }
   1080       }
   1081       //
   1082       // Return since we aready received required data.
   1083       //
   1084       Status = EFI_SUCCESS;
   1085       goto Exit;
   1086     }
   1087 
   1088     if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
   1089       //
   1090       // We received a complete HTTP message, and we don't have more data to return to caller.
   1091       //
   1092       HttpMsg->BodyLength = 0;
   1093       Status = EFI_SUCCESS;
   1094       goto Exit;
   1095     }
   1096   }
   1097 
   1098   ASSERT (HttpInstance->MsgParser != NULL);
   1099 
   1100   //
   1101   // We still need receive more data when there is no cache data and MsgParser is not NULL;
   1102   //
   1103   Status = HttpTcpReceiveBody (Wrap, HttpMsg);
   1104   if (EFI_ERROR (Status)) {
   1105     goto Error;
   1106   }
   1107 
   1108   return Status;
   1109 
   1110 Exit:
   1111   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
   1112   if (Item != NULL) {
   1113     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
   1114   }
   1115   Token->Status = Status;
   1116   gBS->SignalEvent (Token->Event);
   1117   HttpCloseTcpRxEvent (Wrap);
   1118   FreePool (Wrap);
   1119   return Status;
   1120 
   1121 Error2:
   1122   NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
   1123 
   1124 Error:
   1125   HttpTcpTokenCleanup (Wrap);
   1126 
   1127   if (HttpHeaders != NULL) {
   1128     FreePool (HttpHeaders);
   1129   }
   1130 
   1131   if (HttpMsg->Headers != NULL) {
   1132     FreePool (HttpMsg->Headers);
   1133   }
   1134 
   1135   if (HttpInstance->CacheBody != NULL) {
   1136     FreePool (HttpInstance->CacheBody);
   1137     HttpInstance->CacheBody = NULL;
   1138   }
   1139 
   1140   Token->Status = Status;
   1141   gBS->SignalEvent (Token->Event);
   1142 
   1143   return Status;
   1144 
   1145 }
   1146 
   1147 
   1148 /**
   1149   The Response() function queues an HTTP response to this HTTP instance, similar to
   1150   Receive() function in the EFI TCP driver. When the HTTP request is sent successfully,
   1151   or if there is an error, Status in token will be updated and Event will be signaled.
   1152 
   1153   The HTTP driver will queue a receive token to the underlying TCP instance. When data
   1154   is received in the underlying TCP instance, the data will be parsed and Token will
   1155   be populated with the response data. If the data received from the remote host
   1156   contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
   1157   (asynchronously) for more data to be sent from the remote host before signaling
   1158   Event in Token.
   1159 
   1160   It is the responsibility of the caller to allocate a buffer for Body and specify the
   1161   size in BodyLength. If the remote host provides a response that contains a content
   1162   body, up to BodyLength bytes will be copied from the receive buffer into Body and
   1163   BodyLength will be updated with the amount of bytes received and copied to Body. This
   1164   allows the client to download a large file in chunks instead of into one contiguous
   1165   block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
   1166   non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
   1167   token to underlying TCP instance. If data arrives in the receive buffer, up to
   1168   BodyLength bytes of data will be copied to Body. The HTTP driver will then update
   1169   BodyLength with the amount of bytes received and copied to Body.
   1170 
   1171   If the HTTP driver does not have an open underlying TCP connection with the host
   1172   specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
   1173   consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
   1174   an open TCP connection between client and host.
   1175 
   1176   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
   1177   @param[in]  Token               Pointer to storage containing HTTP response token.
   1178 
   1179   @retval EFI_SUCCESS             Allocation succeeded.
   1180   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been
   1181                                   initialized.
   1182   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
   1183                                   This is NULL.
   1184                                   Token is NULL.
   1185                                   Token->Message->Headers is NULL.
   1186                                   Token->Message is NULL.
   1187                                   Token->Message->Body is not NULL,
   1188                                   Token->Message->BodyLength is non-zero, and
   1189                                   Token->Message->Data is NULL, but a previous call to
   1190                                   Response() has not been completed successfully.
   1191   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
   1192   @retval EFI_ACCESS_DENIED       An open TCP connection is not present with the host
   1193                                   specified by response URL.
   1194 **/
   1195 EFI_STATUS
   1196 EFIAPI
   1197 EfiHttpResponse (
   1198   IN  EFI_HTTP_PROTOCOL         *This,
   1199   IN  EFI_HTTP_TOKEN            *Token
   1200   )
   1201 {
   1202   EFI_STATUS                    Status;
   1203   EFI_HTTP_MESSAGE              *HttpMsg;
   1204   HTTP_PROTOCOL                 *HttpInstance;
   1205   HTTP_TOKEN_WRAP               *Wrap;
   1206 
   1207   if ((This == NULL) || (Token == NULL)) {
   1208     return EFI_INVALID_PARAMETER;
   1209   }
   1210 
   1211   HttpMsg = Token->Message;
   1212   if (HttpMsg == NULL) {
   1213     return EFI_INVALID_PARAMETER;
   1214   }
   1215 
   1216   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
   1217   ASSERT (HttpInstance != NULL);
   1218 
   1219   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
   1220     return EFI_NOT_STARTED;
   1221   }
   1222 
   1223   //
   1224   // Check whether the token already existed.
   1225   //
   1226   if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {
   1227     return EFI_ACCESS_DENIED;
   1228   }
   1229 
   1230   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
   1231   if (Wrap == NULL) {
   1232     return EFI_OUT_OF_RESOURCES;
   1233   }
   1234 
   1235   Wrap->HttpInstance = HttpInstance;
   1236   Wrap->HttpToken    = Token;
   1237 
   1238   Status = HttpCreateTcpRxEvent (Wrap);
   1239   if (EFI_ERROR (Status)) {
   1240     goto Error;
   1241   }
   1242 
   1243   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);
   1244   if (EFI_ERROR (Status)) {
   1245     goto Error;
   1246   }
   1247 
   1248   //
   1249   // If already have pending RxTokens, return directly.
   1250   //
   1251   if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {
   1252     return EFI_SUCCESS;
   1253   }
   1254 
   1255   return HttpResponseWorker (Wrap);
   1256 
   1257 Error:
   1258   if (Wrap != NULL) {
   1259     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
   1260       gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
   1261     }
   1262 
   1263     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
   1264       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
   1265     }
   1266     FreePool (Wrap);
   1267   }
   1268 
   1269   return Status;
   1270 }
   1271 
   1272 /**
   1273   The Poll() function can be used by network drivers and applications to increase the
   1274   rate that data packets are moved between the communication devices and the transmit
   1275   and receive queues.
   1276 
   1277   In some systems, the periodic timer event in the managed network driver may not poll
   1278   the underlying communications device fast enough to transmit and/or receive all data
   1279   packets without missing incoming packets or dropping outgoing packets. Drivers and
   1280   applications that are experiencing packet loss should try calling the Poll() function
   1281   more often.
   1282 
   1283   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
   1284 
   1285   @retval EFI_SUCCESS             Incoming or outgoing data was processed.
   1286   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
   1287   @retval EFI_INVALID_PARAMETER   This is NULL.
   1288   @retval EFI_NOT_READY           No incoming or outgoing data is processed.
   1289   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
   1290 
   1291 **/
   1292 EFI_STATUS
   1293 EFIAPI
   1294 EfiHttpPoll (
   1295   IN  EFI_HTTP_PROTOCOL         *This
   1296   )
   1297 {
   1298   EFI_STATUS                    Status;
   1299   HTTP_PROTOCOL                 *HttpInstance;
   1300 
   1301   if (This == NULL) {
   1302     return EFI_INVALID_PARAMETER;
   1303   }
   1304 
   1305   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
   1306   ASSERT (HttpInstance != NULL);
   1307 
   1308   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
   1309     return EFI_NOT_STARTED;
   1310   }
   1311 
   1312   if (HttpInstance->LocalAddressIsIPv6) {
   1313     if (HttpInstance->Tcp6 == NULL) {
   1314       return EFI_NOT_STARTED;
   1315     }
   1316     Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
   1317   } else {
   1318     if (HttpInstance->Tcp4 == NULL) {
   1319       return EFI_NOT_STARTED;
   1320     }
   1321     Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
   1322   }
   1323 
   1324   DispatchDpc ();
   1325 
   1326   return Status;
   1327 }
   1328