REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3496 Add new EDKII_HTTP_CALLBACK_PROTOCOL in NetworkPkg, Send HTTP Events via EDKII_HTTP_CALLBACK_PROTOCOL when Dns/ConnectTcp/TlsConnectSession/InitSession occurs. Signed-off-by: Heng Luo <heng.luo@intel.com> Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com> Reviewed-by: Jiaxin Wu <jiaxin.wu@intel.com>
		
			
				
	
	
		
			1683 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1683 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
 | |
| 
 | |
|   Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR>
 | |
|   (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "HttpDriver.h"
 | |
| 
 | |
| EFI_HTTP_PROTOCOL  mEfiHttpTemplate = {
 | |
|   EfiHttpGetModeData,
 | |
|   EfiHttpConfigure,
 | |
|   EfiHttpRequest,
 | |
|   EfiHttpCancel,
 | |
|   EfiHttpResponse,
 | |
|   EfiHttpPoll
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Returns the operational parameters for the current HTTP child instance.
 | |
| 
 | |
|   The GetModeData() function is used to read the current mode data (operational
 | |
|   parameters) for this HTTP protocol instance.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
|   @param[out] HttpConfigData      Point to buffer for operational parameters of this
 | |
|                                   HTTP instance. It is the responsibility of the caller
 | |
|                                   to allocate the memory for HttpConfigData and
 | |
|                                   HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact,
 | |
|                                   it is recommended to allocate sufficient memory to record
 | |
|                                   IPv6Node since it is big enough for all possibilities.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Operation succeeded.
 | |
|   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
 | |
|                                   This is NULL.
 | |
|                                   HttpConfigData is NULL.
 | |
|                                   HttpConfigData->AccessPoint.IPv4Node or
 | |
|                                   HttpConfigData->AccessPoint.IPv6Node is NULL.
 | |
|   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpGetModeData (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This,
 | |
|   OUT EFI_HTTP_CONFIG_DATA      *HttpConfigData
 | |
|   )
 | |
| {
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if ((This == NULL) || (HttpConfigData == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
| 
 | |
|   if ((HttpConfigData->AccessPoint.IPv6Node == NULL) ||
 | |
|       (HttpConfigData->AccessPoint.IPv4Node == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   HttpConfigData->HttpVersion        = HttpInstance->HttpVersion;
 | |
|   HttpConfigData->TimeOutMillisec    = HttpInstance->TimeOutMillisec;
 | |
|   HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;
 | |
| 
 | |
|   if (HttpInstance->LocalAddressIsIPv6) {
 | |
|     CopyMem (
 | |
|       HttpConfigData->AccessPoint.IPv6Node,
 | |
|       &HttpInstance->Ipv6Node,
 | |
|       sizeof (HttpInstance->Ipv6Node)
 | |
|     );
 | |
|   } else {
 | |
|     CopyMem (
 | |
|       HttpConfigData->AccessPoint.IPv4Node,
 | |
|       &HttpInstance->IPv4Node,
 | |
|       sizeof (HttpInstance->IPv4Node)
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize or brutally reset the operational parameters for this EFI HTTP instance.
 | |
| 
 | |
|   The Configure() function does the following:
 | |
|   When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring
 | |
|   timeout, local address, port, etc.
 | |
|   When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active
 | |
|   connections with remote hosts, canceling all asynchronous tokens, and flush request
 | |
|   and response buffers without informing the appropriate hosts.
 | |
| 
 | |
|   No other EFI HTTP function can be executed by this instance until the Configure()
 | |
|   function is executed and returns successfully.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
|   @param[in]  HttpConfigData      Pointer to the configure data to configure the instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Operation succeeded.
 | |
|   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
 | |
|                                   This is NULL.
 | |
|                                   HttpConfigData->LocalAddressIsIPv6 is FALSE and
 | |
|                                   HttpConfigData->AccessPoint.IPv4Node is NULL.
 | |
|                                   HttpConfigData->LocalAddressIsIPv6 is TRUE and
 | |
|                                   HttpConfigData->AccessPoint.IPv6Node is NULL.
 | |
|   @retval EFI_ALREADY_STARTED     Reinitialize this HTTP instance without calling
 | |
|                                   Configure() with NULL to reset it.
 | |
|   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
 | |
|   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources when
 | |
|                                   executing Configure().
 | |
|   @retval EFI_UNSUPPORTED         One or more options in HttpConfigData are not supported
 | |
|                                   in the implementation.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpConfigure (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This,
 | |
|   IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData OPTIONAL
 | |
|   )
 | |
| {
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
|   EFI_STATUS                    Status;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (This == NULL ||
 | |
|       (HttpConfigData != NULL &&
 | |
|        ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
 | |
|         (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
|   ASSERT (HttpInstance->Service != NULL);
 | |
| 
 | |
|   if (HttpConfigData != NULL) {
 | |
| 
 | |
|     if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Now configure this HTTP instance.
 | |
|     //
 | |
|     if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {
 | |
|       return EFI_ALREADY_STARTED;
 | |
|     }
 | |
| 
 | |
|     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;
 | |
|     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;
 | |
|     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
 | |
| 
 | |
|     if (HttpConfigData->LocalAddressIsIPv6) {
 | |
|       CopyMem (
 | |
|         &HttpInstance->Ipv6Node,
 | |
|         HttpConfigData->AccessPoint.IPv6Node,
 | |
|         sizeof (HttpInstance->Ipv6Node)
 | |
|         );
 | |
|     } else {
 | |
|       CopyMem (
 | |
|         &HttpInstance->IPv4Node,
 | |
|         HttpConfigData->AccessPoint.IPv4Node,
 | |
|         sizeof (HttpInstance->IPv4Node)
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Creat Tcp child
 | |
|     //
 | |
|     Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   } else {
 | |
|     //
 | |
|     // Reset all the resources related to HttpInstance.
 | |
|     //
 | |
|     HttpCleanProtocol (HttpInstance);
 | |
|     HttpInstance->State = HTTP_STATE_UNCONFIGED;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The Request() function queues an HTTP request to this HTTP instance.
 | |
| 
 | |
|   Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent
 | |
|   successfully, or if there is an error, Status in token will be updated and Event will
 | |
|   be signaled.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
|   @param[in]  Token               Pointer to storage containing HTTP request token.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Outgoing data was processed.
 | |
|   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
 | |
|   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
 | |
|   @retval EFI_TIMEOUT             Data was dropped out of the transmit or receive queue.
 | |
|   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
 | |
|   @retval EFI_UNSUPPORTED         The HTTP method is not supported in current
 | |
|                                   implementation.
 | |
|   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
 | |
|                                   This is NULL.
 | |
|                                   Token is NULL.
 | |
|                                   Token->Message is NULL.
 | |
|                                   Token->Message->Body is not NULL,
 | |
|                                   Token->Message->BodyLength is non-zero, and
 | |
|                                   Token->Message->Data is NULL, but a previous call to
 | |
|                                   Request()has not been completed successfully.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpRequest (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This,
 | |
|   IN  EFI_HTTP_TOKEN            *Token
 | |
|   )
 | |
| {
 | |
|   EFI_HTTP_MESSAGE              *HttpMsg;
 | |
|   EFI_HTTP_REQUEST_DATA         *Request;
 | |
|   VOID                          *UrlParser;
 | |
|   EFI_STATUS                    Status;
 | |
|   CHAR8                         *HostName;
 | |
|   UINTN                         HostNameSize;
 | |
|   UINT16                        RemotePort;
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
|   BOOLEAN                       Configure;
 | |
|   BOOLEAN                       ReConfigure;
 | |
|   BOOLEAN                       TlsConfigure;
 | |
|   CHAR8                         *RequestMsg;
 | |
|   CHAR8                         *Url;
 | |
|   UINTN                         UrlLen;
 | |
|   CHAR16                        *HostNameStr;
 | |
|   HTTP_TOKEN_WRAP               *Wrap;
 | |
|   CHAR8                         *FileUrl;
 | |
|   UINTN                         RequestMsgSize;
 | |
|   EFI_HANDLE                    ImageHandle;
 | |
| 
 | |
|   //
 | |
|   // Initializations
 | |
|   //
 | |
|   Url = NULL;
 | |
|   UrlParser = NULL;
 | |
|   RemotePort = 0;
 | |
|   HostName = NULL;
 | |
|   RequestMsg = NULL;
 | |
|   HostNameStr = NULL;
 | |
|   Wrap = NULL;
 | |
|   FileUrl = NULL;
 | |
|   TlsConfigure = FALSE;
 | |
| 
 | |
|   if ((This == NULL) || (Token == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpMsg = Token->Message;
 | |
|   if (HttpMsg == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Request = HttpMsg->Data.Request;
 | |
| 
 | |
|   //
 | |
|   // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation.
 | |
|   //
 | |
|   if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
 | |
|       (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodDelete) &&
 | |
|       (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost) &&
 | |
|       (Request->Method != HttpMethodPatch)) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
| 
 | |
|   //
 | |
|   // Capture the method into HttpInstance.
 | |
|   //
 | |
|   if (Request != NULL) {
 | |
|     HttpInstance->Method = Request->Method;
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (Request == NULL) {
 | |
|     //
 | |
|     // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation)
 | |
|     //
 | |
|     if ((HttpInstance->Method != HttpMethodPut) &&
 | |
|         (HttpInstance->Method != HttpMethodPost) &&
 | |
|         (HttpInstance->Method != HttpMethodPatch)) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // For PUT/POST/PATCH, we need to have the TCP already configured. Bail out if it is not!
 | |
|     //
 | |
|     if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // We need to have the Message Body for sending the HTTP message across in these cases.
 | |
|     //
 | |
|     if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Use existing TCP instance to transmit the packet.
 | |
|     //
 | |
|     Configure   = FALSE;
 | |
|     ReConfigure = FALSE;
 | |
|   } else {
 | |
|     //
 | |
|     // Check whether the token already existed.
 | |
|     //
 | |
|     if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
 | |
|       return EFI_ACCESS_DENIED;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Parse the URI of the remote host.
 | |
|     //
 | |
|     Url = HttpInstance->Url;
 | |
|     UrlLen = StrLen (Request->Url) + 1;
 | |
|     if (UrlLen > HTTP_URL_BUFFER_LEN) {
 | |
|       Url = AllocateZeroPool (UrlLen);
 | |
|       if (Url == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
|       FreePool (HttpInstance->Url);
 | |
|       HttpInstance->Url = Url;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);
 | |
| 
 | |
|     //
 | |
|     // From the information in Url, the HTTP instance will
 | |
|     // be able to determine whether to use http or https.
 | |
|     //
 | |
|     HttpInstance->UseHttps = IsHttpsUrl (Url);
 | |
| 
 | |
|     //
 | |
|     // HTTP is disabled, return directly if the URI is not HTTPS.
 | |
|     //
 | |
|     if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {
 | |
| 
 | |
|       DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));
 | |
| 
 | |
|       return EFI_ACCESS_DENIED;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check whether we need to create Tls child and open the TLS protocol.
 | |
|     //
 | |
|     if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {
 | |
|       //
 | |
|       // Use TlsSb to create Tls child and open the TLS protocol.
 | |
|       //
 | |
|       if (HttpInstance->LocalAddressIsIPv6) {
 | |
|         ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;
 | |
|       } else {
 | |
|         ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;
 | |
|       }
 | |
| 
 | |
|       HttpInstance->TlsChildHandle = TlsCreateChild (
 | |
|                                        ImageHandle,
 | |
|                                        &(HttpInstance->TlsSb),
 | |
|                                        &(HttpInstance->Tls),
 | |
|                                        &(HttpInstance->TlsConfiguration)
 | |
|                                        );
 | |
|       if (HttpInstance->TlsChildHandle == NULL) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
| 
 | |
|       TlsConfigure = TRUE;
 | |
|     }
 | |
| 
 | |
|     UrlParser = NULL;
 | |
|     Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error1;
 | |
|     }
 | |
| 
 | |
|     Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error1;
 | |
|     }
 | |
| 
 | |
|     if (HttpInstance->LocalAddressIsIPv6) {
 | |
|       HostNameSize = AsciiStrSize (HostName);
 | |
| 
 | |
|       if (HostNameSize > 2 && HostName[0] == '[' && HostName[HostNameSize - 2] == ']') {
 | |
|         //
 | |
|         // HostName format is expressed as IPv6, so, remove '[' and ']'.
 | |
|         //
 | |
|         HostNameSize -= 2;
 | |
|         CopyMem (HostName, HostName + 1, HostNameSize - 1);
 | |
|         HostName[HostNameSize - 1] = '\0';
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       if (HttpInstance->UseHttps) {
 | |
|         RemotePort = HTTPS_DEFAULT_PORT;
 | |
|       } else {
 | |
|         RemotePort = HTTP_DEFAULT_PORT;
 | |
|       }
 | |
|     }
 | |
|     //
 | |
|     // If Configure is TRUE, it indicates the first time to call Request();
 | |
|     // If ReConfigure is TRUE, it indicates the request URL is not same
 | |
|     // with the previous call to Request();
 | |
|     //
 | |
|     Configure   = TRUE;
 | |
|     ReConfigure = TRUE;
 | |
| 
 | |
|     if (HttpInstance->RemoteHost == NULL) {
 | |
|       //
 | |
|       // Request() is called the first time.
 | |
|       //
 | |
|       ReConfigure = FALSE;
 | |
|     } else {
 | |
|       if ((HttpInstance->RemotePort == RemotePort) &&
 | |
|           (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) &&
 | |
|           (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
 | |
|                                        !TlsConfigure &&
 | |
|                                        HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {
 | |
|         //
 | |
|         // Host Name and port number of the request URL are the same with previous call to Request().
 | |
|         // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
 | |
|         // Check whether previous TCP packet sent out.
 | |
|         //
 | |
| 
 | |
|         if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
 | |
|           //
 | |
|           // Wrap the HTTP token in HTTP_TOKEN_WRAP
 | |
|           //
 | |
|           Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
 | |
|           if (Wrap == NULL) {
 | |
|             Status = EFI_OUT_OF_RESOURCES;
 | |
|             goto Error1;
 | |
|           }
 | |
| 
 | |
|           Wrap->HttpToken    = Token;
 | |
|           Wrap->HttpInstance = HttpInstance;
 | |
| 
 | |
|           Status = HttpCreateTcpTxEvent (Wrap);
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             goto Error1;
 | |
|           }
 | |
| 
 | |
|           Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             goto Error1;
 | |
|           }
 | |
| 
 | |
|           Wrap->TcpWrap.Method = Request->Method;
 | |
| 
 | |
|           FreePool (HostName);
 | |
| 
 | |
|           HttpUrlFreeParser (UrlParser);
 | |
| 
 | |
|           //
 | |
|           // Queue the HTTP token and return.
 | |
|           //
 | |
|           return EFI_SUCCESS;
 | |
|         } else {
 | |
|           //
 | |
|           // Use existing TCP instance to transmit the packet.
 | |
|           //
 | |
|           Configure   = FALSE;
 | |
|           ReConfigure = FALSE;
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // Need close existing TCP instance and create a new TCP instance for data transmit.
 | |
|         //
 | |
|         if (HttpInstance->RemoteHost != NULL) {
 | |
|           FreePool (HttpInstance->RemoteHost);
 | |
|           HttpInstance->RemoteHost = NULL;
 | |
|           HttpInstance->RemotePort = 0;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Configure) {
 | |
|     //
 | |
|     // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
 | |
|     //
 | |
|     if (!HttpInstance->LocalAddressIsIPv6) {
 | |
|       Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
 | |
|     } else {
 | |
|       Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
 | |
|     }
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       HostNameSize = AsciiStrSize (HostName);
 | |
|       HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
 | |
|       if (HostNameStr == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto Error1;
 | |
|       }
 | |
| 
 | |
|       AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
 | |
|       if (!HttpInstance->LocalAddressIsIPv6) {
 | |
|         Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
 | |
|       } else {
 | |
|         Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);
 | |
|       }
 | |
|       HttpNotify (HttpEventDns, Status);
 | |
| 
 | |
|       FreePool (HostNameStr);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         DEBUG ((EFI_D_ERROR, "Error: Could not retrieve the host address from DNS server.\n"));
 | |
|         goto Error1;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Save the RemotePort and RemoteHost.
 | |
|     //
 | |
|     ASSERT (HttpInstance->RemoteHost == NULL);
 | |
|     HttpInstance->RemotePort = RemotePort;
 | |
|     HttpInstance->RemoteHost = HostName;
 | |
|     HostName = NULL;
 | |
|   }
 | |
| 
 | |
|   if (ReConfigure) {
 | |
|     //
 | |
|     // The request URL is different from previous calls to Request(), close existing TCP instance.
 | |
|     //
 | |
|     if (!HttpInstance->LocalAddressIsIPv6) {
 | |
|       ASSERT (HttpInstance->Tcp4 != NULL);
 | |
|     } else {
 | |
|       ASSERT (HttpInstance->Tcp6 != NULL);
 | |
|     }
 | |
| 
 | |
|     if (HttpInstance->UseHttps && !TlsConfigure) {
 | |
|       Status = TlsCloseSession (HttpInstance);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error1;
 | |
|       }
 | |
| 
 | |
|       TlsCloseTxRxEvent (HttpInstance);
 | |
|     }
 | |
| 
 | |
|     HttpCloseConnection (HttpInstance);
 | |
|     EfiHttpCancel (This, NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Wrap the HTTP token in HTTP_TOKEN_WRAP
 | |
|   //
 | |
|   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
 | |
|   if (Wrap == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error1;
 | |
|   }
 | |
| 
 | |
|   Wrap->HttpToken      = Token;
 | |
|   Wrap->HttpInstance   = HttpInstance;
 | |
|   if (Request != NULL) {
 | |
|     Wrap->TcpWrap.Method = Request->Method;
 | |
|   }
 | |
| 
 | |
|   Status = HttpInitSession (
 | |
|              HttpInstance,
 | |
|              Wrap,
 | |
|              Configure || ReConfigure,
 | |
|              TlsConfigure
 | |
|              );
 | |
|   HttpNotify (HttpEventInitSession, Status);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Error2;
 | |
|   }
 | |
| 
 | |
|   if (!Configure && !ReConfigure && !TlsConfigure) {
 | |
|     //
 | |
|     // For the new HTTP token, create TX TCP token events.
 | |
|     //
 | |
|     Status = HttpCreateTcpTxEvent (Wrap);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create request message.
 | |
|   //
 | |
|   FileUrl = Url;
 | |
|   if (Url != NULL && *FileUrl != '/') {
 | |
|     //
 | |
|     // Convert the absolute-URI to the absolute-path
 | |
|     //
 | |
|     while (*FileUrl != ':') {
 | |
|       FileUrl++;
 | |
|     }
 | |
|     if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
 | |
|       FileUrl += 3;
 | |
|       while (*FileUrl != '/') {
 | |
|         FileUrl++;
 | |
|       }
 | |
|     } else {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       goto Error3;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
 | |
| 
 | |
|   if (EFI_ERROR (Status) || NULL == RequestMsg) {
 | |
|     goto Error3;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Every request we insert a TxToken and a response call would remove the TxToken.
 | |
|   // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a
 | |
|   // continuous request without a response call. So, in such cases, where Request
 | |
|   // structure is NULL, we would not insert a TxToken.
 | |
|   //
 | |
|   if (Request != NULL) {
 | |
|     Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error4;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Transmit the request message.
 | |
|   //
 | |
|   Status = HttpTransmitTcp (
 | |
|              HttpInstance,
 | |
|              Wrap,
 | |
|              (UINT8*) RequestMsg,
 | |
|              RequestMsgSize
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Error5;
 | |
|   }
 | |
| 
 | |
|   DispatchDpc ();
 | |
| 
 | |
|   if (HostName != NULL) {
 | |
|     FreePool (HostName);
 | |
|   }
 | |
| 
 | |
|   if (UrlParser != NULL) {
 | |
|     HttpUrlFreeParser (UrlParser);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| Error5:
 | |
|   //
 | |
|   // We would have inserted a TxToken only if Request structure is not NULL.
 | |
|   // Hence check before we do a remove in this error case.
 | |
|   //
 | |
|   if (Request != NULL) {
 | |
|     NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
 | |
|   }
 | |
| 
 | |
| Error4:
 | |
|   if (RequestMsg != NULL) {
 | |
|     FreePool (RequestMsg);
 | |
|   }
 | |
| 
 | |
| Error3:
 | |
|   if (HttpInstance->UseHttps) {
 | |
|     TlsCloseSession (HttpInstance);
 | |
|     TlsCloseTxRxEvent (HttpInstance);
 | |
|   }
 | |
| 
 | |
| Error2:
 | |
|   HttpCloseConnection (HttpInstance);
 | |
| 
 | |
|   HttpCloseTcpConnCloseEvent (HttpInstance);
 | |
|   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
 | |
|     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
 | |
|     Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;
 | |
|   }
 | |
|   if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {
 | |
|     gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
 | |
|     Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;
 | |
|   }
 | |
| 
 | |
| Error1:
 | |
|   if (HostName != NULL) {
 | |
|     FreePool (HostName);
 | |
|   }
 | |
|   if (Wrap != NULL) {
 | |
|     FreePool (Wrap);
 | |
|   }
 | |
|   if (UrlParser != NULL) {
 | |
|     HttpUrlFreeParser (UrlParser);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cancel a user's Token.
 | |
| 
 | |
|   @param[in]  Map                The HTTP instance's token queue.
 | |
|   @param[in]  Item               Object container for one HTTP token and token's wrap.
 | |
|   @param[in]  Context            The user's token to cancel.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Continue to check the next Item.
 | |
|   @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| HttpCancelTokens (
 | |
|   IN NET_MAP                *Map,
 | |
|   IN NET_MAP_ITEM           *Item,
 | |
|   IN VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   EFI_HTTP_TOKEN            *Token;
 | |
|   HTTP_TOKEN_WRAP           *Wrap;
 | |
|   HTTP_PROTOCOL             *HttpInstance;
 | |
| 
 | |
|   Token = (EFI_HTTP_TOKEN *) Context;
 | |
| 
 | |
|   //
 | |
|   // Return EFI_SUCCESS to check the next item in the map if
 | |
|   // this one doesn't match.
 | |
|   //
 | |
|   if ((Token != NULL) && (Token != Item->Key)) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;
 | |
|   ASSERT (Wrap != NULL);
 | |
|   HttpInstance = Wrap->HttpInstance;
 | |
| 
 | |
|   if (!HttpInstance->LocalAddressIsIPv6) {
 | |
|     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
 | |
|       //
 | |
|       // Cancel the Token before close its Event.
 | |
|       //
 | |
|       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
 | |
| 
 | |
|       //
 | |
|       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
 | |
|       //
 | |
|       DispatchDpc ();
 | |
|     }
 | |
|   } else {
 | |
|     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
 | |
|       //
 | |
|       // Cancel the Token before close its Event.
 | |
|       //
 | |
|       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
 | |
| 
 | |
|       //
 | |
|       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
 | |
|       //
 | |
|       DispatchDpc ();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If only one item is to be cancel, return EFI_ABORTED to stop
 | |
|   // iterating the map any more.
 | |
|   //
 | |
|   if (Token != NULL) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cancel the user's receive/transmit request. It is the worker function of
 | |
|   EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
 | |
|   token.
 | |
| 
 | |
|   @param[in]  HttpInstance       Pointer to HTTP_PROTOCOL structure.
 | |
|   @param[in]  Token              The token to cancel. If NULL, all token will be
 | |
|                                  cancelled.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The token is cancelled.
 | |
|   @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.
 | |
|   @retval Others                 Other error as indicated.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| HttpCancel (
 | |
|   IN  HTTP_PROTOCOL             *HttpInstance,
 | |
|   IN  EFI_HTTP_TOKEN            *Token
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
| 
 | |
|   //
 | |
|   // First check the tokens queued by EfiHttpRequest().
 | |
|   //
 | |
|   Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     if (Token != NULL) {
 | |
|       if (Status == EFI_ABORTED) {
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
|     } else {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!HttpInstance->UseHttps) {
 | |
|     //
 | |
|     // Then check the tokens queued by EfiHttpResponse(), except for Https.
 | |
|     //
 | |
|     Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       if (Token != NULL) {
 | |
|         if (Status == EFI_ABORTED) {
 | |
|           return EFI_SUCCESS;
 | |
|         } else {
 | |
|           return EFI_NOT_FOUND;
 | |
|         }
 | |
|       } else {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (!HttpInstance->LocalAddressIsIPv6) {
 | |
|       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
 | |
|     } else {
 | |
|       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Abort an asynchronous HTTP request or response token.
 | |
| 
 | |
|   The Cancel() function aborts a pending HTTP request or response transaction. If
 | |
|   Token is not NULL and the token is in transmit or receive queues when it is being
 | |
|   cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
 | |
|   be signaled. If the token is not in one of the queues, which usually means that the
 | |
|   asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
 | |
|   all asynchronous tokens issued by Request() or Response() will be aborted.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
|   @param[in]  Token               Point to storage containing HTTP request or response
 | |
|                                   token.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Request and Response queues are successfully flushed.
 | |
|   @retval EFI_INVALID_PARAMETER   This is NULL.
 | |
|   @retval EFI_NOT_STARTED         This instance hasn't been configured.
 | |
|   @retval EFI_NOT_FOUND           The asynchronous request or response token is not
 | |
|                                   found.
 | |
|   @retval EFI_UNSUPPORTED         The implementation does not support this function.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpCancel (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This,
 | |
|   IN  EFI_HTTP_TOKEN            *Token
 | |
|   )
 | |
| {
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
| 
 | |
|   if (This == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
| 
 | |
|   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   return HttpCancel (HttpInstance, Token);
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   A callback function to intercept events during message parser.
 | |
| 
 | |
|   This function will be invoked during HttpParseMessageBody() with various events type. An error
 | |
|   return status of the callback function will cause the HttpParseMessageBody() aborted.
 | |
| 
 | |
|   @param[in]    EventType          Event type of this callback call.
 | |
|   @param[in]    Data               A pointer to data buffer.
 | |
|   @param[in]    Length             Length in bytes of the Data.
 | |
|   @param[in]    Context            Callback context set by HttpInitMsgParser().
 | |
| 
 | |
|   @retval EFI_SUCCESS              Continue to parser the message body.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| HttpBodyParserCallback (
 | |
|   IN HTTP_BODY_PARSE_EVENT      EventType,
 | |
|   IN CHAR8                      *Data,
 | |
|   IN UINTN                      Length,
 | |
|   IN VOID                       *Context
 | |
|   )
 | |
| {
 | |
|   HTTP_CALLBACK_DATA            *CallbackData;
 | |
|   HTTP_TOKEN_WRAP               *Wrap;
 | |
|   UINTN                         BodyLength;
 | |
|   CHAR8                         *Body;
 | |
| 
 | |
|   if (EventType != BodyParseEventOnComplete) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (Data == NULL || Length != 0 || Context == NULL) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   CallbackData = (HTTP_CALLBACK_DATA *) Context;
 | |
| 
 | |
|   Wrap       = (HTTP_TOKEN_WRAP *) (CallbackData->Wrap);
 | |
|   Body       = CallbackData->ParseData;
 | |
|   BodyLength = CallbackData->ParseDataLength;
 | |
| 
 | |
|   if (Data < Body + BodyLength) {
 | |
|     Wrap->HttpInstance->NextMsg = Data;
 | |
|   } else {
 | |
|     Wrap->HttpInstance->NextMsg = NULL;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The work function of EfiHttpResponse().
 | |
| 
 | |
|   @param[in]  Wrap                Pointer to HTTP token's wrap data.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Allocation succeeded.
 | |
|   @retval EFI_OUT_OF_RESOURCES    Failed to complete the operation due to lack of resources.
 | |
|   @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or
 | |
|                                   the EFI_HTTP_UTILITIES_PROTOCOL is not available.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| HttpResponseWorker (
 | |
|   IN  HTTP_TOKEN_WRAP           *Wrap
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   EFI_HTTP_MESSAGE              *HttpMsg;
 | |
|   CHAR8                         *EndofHeader;
 | |
|   CHAR8                         *HttpHeaders;
 | |
|   UINTN                         SizeofHeaders;
 | |
|   UINTN                         BufferSize;
 | |
|   UINTN                         StatusCode;
 | |
|   CHAR8                         *Tmp;
 | |
|   CHAR8                         *HeaderTmp;
 | |
|   CHAR8                         *StatusCodeStr;
 | |
|   UINTN                         BodyLen;
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
|   EFI_HTTP_TOKEN                *Token;
 | |
|   NET_MAP_ITEM                  *Item;
 | |
|   HTTP_TOKEN_WRAP               *ValueInItem;
 | |
|   UINTN                         HdrLen;
 | |
|   NET_FRAGMENT                  Fragment;
 | |
|   UINT32                        TimeoutValue;
 | |
| 
 | |
|   if (Wrap == NULL || Wrap->HttpInstance == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = Wrap->HttpInstance;
 | |
|   Token = Wrap->HttpToken;
 | |
|   HttpMsg = Token->Message;
 | |
| 
 | |
|   HttpInstance->EndofHeader = NULL;
 | |
|   HttpInstance->HttpHeaders = NULL;
 | |
|   HttpMsg->Headers          = NULL;
 | |
|   HttpHeaders               = NULL;
 | |
|   SizeofHeaders             = 0;
 | |
|   BufferSize                = 0;
 | |
|   EndofHeader               = NULL;
 | |
|   ValueInItem               = NULL;
 | |
|   Fragment.Len              = 0;
 | |
|   Fragment.Bulk             = NULL;
 | |
| 
 | |
|   if (HttpMsg->Data.Response != NULL) {
 | |
|     //
 | |
|     // Check whether we have cached header from previous call.
 | |
|     //
 | |
|     if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
 | |
|       //
 | |
|       // The data is stored at [NextMsg, CacheBody + CacheLen].
 | |
|       //
 | |
|       HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
 | |
|       HttpHeaders = AllocateZeroPool (HdrLen);
 | |
|       if (HttpHeaders == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
 | |
|       FreePool (HttpInstance->CacheBody);
 | |
|       HttpInstance->CacheBody   = NULL;
 | |
|       HttpInstance->NextMsg     = NULL;
 | |
|       HttpInstance->CacheOffset = 0;
 | |
|       SizeofHeaders = HdrLen;
 | |
|       BufferSize = HttpInstance->CacheLen;
 | |
| 
 | |
|       //
 | |
|       // Check whether we cached the whole HTTP headers.
 | |
|       //
 | |
|       EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR);
 | |
|     }
 | |
| 
 | |
|     HttpInstance->EndofHeader = &EndofHeader;
 | |
|     HttpInstance->HttpHeaders = &HttpHeaders;
 | |
| 
 | |
| 
 | |
|     if (HttpInstance->TimeoutEvent == NULL) {
 | |
|       //
 | |
|       // Create TimeoutEvent for response
 | |
|       //
 | |
|       Status = gBS->CreateEvent (
 | |
|                       EVT_TIMER,
 | |
|                       TPL_CALLBACK,
 | |
|                       NULL,
 | |
|                       NULL,
 | |
|                       &HttpInstance->TimeoutEvent
 | |
|                       );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Get HTTP timeout value
 | |
|     //
 | |
|     TimeoutValue = PcdGet32 (PcdHttpIoTimeout);
 | |
| 
 | |
|     //
 | |
|     // Start the timer, and wait Timeout seconds to receive the header packet.
 | |
|     //
 | |
|     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, TimeoutValue * TICKS_PER_MS);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);
 | |
| 
 | |
|     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     ASSERT (HttpHeaders != NULL);
 | |
| 
 | |
|     //
 | |
|     // Cache the part of body.
 | |
|     //
 | |
|     BodyLen = BufferSize - (EndofHeader - HttpHeaders);
 | |
|     if (BodyLen > 0) {
 | |
|       if (HttpInstance->CacheBody != NULL) {
 | |
|         FreePool (HttpInstance->CacheBody);
 | |
|       }
 | |
| 
 | |
|       HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
 | |
|       if (HttpInstance->CacheBody == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
 | |
|       HttpInstance->CacheLen = BodyLen;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Search for Status Code.
 | |
|     //
 | |
|     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
 | |
|     if (StatusCodeStr == NULL) {
 | |
|       Status = EFI_NOT_READY;
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);
 | |
| 
 | |
|     //
 | |
|     // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
 | |
|     //
 | |
|     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
 | |
|     if (Tmp == NULL) {
 | |
|       Status = EFI_NOT_READY;
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // We could have response with just a HTTP message and no headers. For Example,
 | |
|     // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
 | |
|     // method. A "\r\n" following Tmp string again would indicate an end. Compare and
 | |
|     // set SizeofHeaders to 0.
 | |
|     //
 | |
|     Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
 | |
|     if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
 | |
|       SizeofHeaders = 0;
 | |
|     } else {
 | |
|       SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
 | |
|     }
 | |
| 
 | |
|     HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
 | |
|     HttpInstance->StatusCode = StatusCode;
 | |
| 
 | |
|     Status = EFI_NOT_READY;
 | |
|     ValueInItem = NULL;
 | |
| 
 | |
|     //
 | |
|     // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a
 | |
|     // continuous request without a response call. So, we would not do an insert of
 | |
|     // TxToken. After we have sent the complete file, we will call a response to get
 | |
|     // a final response from server. In such a case, we would not have any TxTokens.
 | |
|     // Hence, check that case before doing a NetMapRemoveHead.
 | |
|     //
 | |
|     if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
 | |
|       NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
 | |
|       if (ValueInItem == NULL)  {
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // The first Tx Token not transmitted yet, insert back and return error.
 | |
|       //
 | |
|       if (!ValueInItem->TcpWrap.IsTxDone) {
 | |
|         goto Error2;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (SizeofHeaders != 0) {
 | |
|       HeaderTmp = AllocateZeroPool (SizeofHeaders);
 | |
|       if (HeaderTmp == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto Error2;
 | |
|       }
 | |
| 
 | |
|       CopyMem (HeaderTmp, Tmp, SizeofHeaders);
 | |
|       FreePool (HttpHeaders);
 | |
|       HttpHeaders = HeaderTmp;
 | |
| 
 | |
|       //
 | |
|       // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
 | |
|       //
 | |
|       if (mHttpUtilities == NULL) {
 | |
|         Status = EFI_NOT_READY;
 | |
|         goto Error2;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Parse the HTTP header into array of key/value pairs.
 | |
|       //
 | |
|       Status = mHttpUtilities->Parse (
 | |
|                                  mHttpUtilities,
 | |
|                                  HttpHeaders,
 | |
|                                  SizeofHeaders,
 | |
|                                  &HttpMsg->Headers,
 | |
|                                  &HttpMsg->HeaderCount
 | |
|                                  );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error2;
 | |
|       }
 | |
| 
 | |
|       FreePool (HttpHeaders);
 | |
|       HttpHeaders = NULL;
 | |
| 
 | |
| 
 | |
|       //
 | |
|       // Init message-body parser by header information.
 | |
|       //
 | |
|       Status = HttpInitMsgParser (
 | |
|                  HttpInstance->Method,
 | |
|                  HttpMsg->Data.Response->StatusCode,
 | |
|                  HttpMsg->HeaderCount,
 | |
|                  HttpMsg->Headers,
 | |
|                  HttpBodyParserCallback,
 | |
|                  (VOID *) (&HttpInstance->CallbackData),
 | |
|                  &HttpInstance->MsgParser
 | |
|                  );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error2;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Check whether we received a complete HTTP message.
 | |
|       //
 | |
|       if (HttpInstance->CacheBody != NULL) {
 | |
|         //
 | |
|         // Record the CallbackData data.
 | |
|         //
 | |
|         HttpInstance->CallbackData.Wrap = (VOID *) Wrap;
 | |
|         HttpInstance->CallbackData.ParseData = (VOID *) HttpInstance->CacheBody;
 | |
|         HttpInstance->CallbackData.ParseDataLength = HttpInstance->CacheLen;
 | |
| 
 | |
|         //
 | |
|         // Parse message with CallbackData data.
 | |
|         //
 | |
|         Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           goto Error2;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
 | |
|         //
 | |
|         // Free the MsgParse since we already have a full HTTP message.
 | |
|         //
 | |
|         HttpFreeMsgParser (HttpInstance->MsgParser);
 | |
|         HttpInstance->MsgParser = NULL;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
 | |
|       Status = EFI_SUCCESS;
 | |
|       goto Exit;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Receive the response body.
 | |
|   //
 | |
|   BodyLen = 0;
 | |
| 
 | |
|   //
 | |
|   // First check whether we cached some data.
 | |
|   //
 | |
|   if (HttpInstance->CacheBody != NULL) {
 | |
|     //
 | |
|     // Calculate the length of the cached data.
 | |
|     //
 | |
|     if (HttpInstance->NextMsg != NULL) {
 | |
|       //
 | |
|       // We have a cached HTTP message which includes a part of HTTP header of next message.
 | |
|       //
 | |
|       BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);
 | |
|     } else {
 | |
|       BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
 | |
|     }
 | |
| 
 | |
|     if (BodyLen > 0) {
 | |
|       //
 | |
|       // We have some cached data. Just copy the data and return.
 | |
|       //
 | |
|       if (HttpMsg->BodyLength < BodyLen) {
 | |
|         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
 | |
|         HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
 | |
|       } else {
 | |
|         //
 | |
|         // Copy all cached data out.
 | |
|         //
 | |
|         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
 | |
|         HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
 | |
|         HttpMsg->BodyLength = BodyLen;
 | |
| 
 | |
|         if (HttpInstance->NextMsg == NULL) {
 | |
|           //
 | |
|           // There is no HTTP header of next message. Just free the cache buffer.
 | |
|           //
 | |
|           FreePool (HttpInstance->CacheBody);
 | |
|           HttpInstance->CacheBody   = NULL;
 | |
|           HttpInstance->NextMsg     = NULL;
 | |
|           HttpInstance->CacheOffset = 0;
 | |
|         }
 | |
|       }
 | |
|       //
 | |
|       // Return since we already received required data.
 | |
|       //
 | |
|       Status = EFI_SUCCESS;
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
 | |
|       //
 | |
|       // We received a complete HTTP message, and we don't have more data to return to caller.
 | |
|       //
 | |
|       HttpMsg->BodyLength = 0;
 | |
|       Status = EFI_SUCCESS;
 | |
|       goto Exit;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ASSERT (HttpInstance->MsgParser != NULL);
 | |
| 
 | |
|   //
 | |
|   // We still need receive more data when there is no cache data and MsgParser is not NULL;
 | |
|   //
 | |
|   if (!HttpInstance->UseHttps) {
 | |
|     Status = HttpTcpReceiveBody (Wrap, HttpMsg);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error2;
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     if (HttpInstance->TimeoutEvent == NULL) {
 | |
|       //
 | |
|       // Create TimeoutEvent for response
 | |
|       //
 | |
|       Status = gBS->CreateEvent (
 | |
|                       EVT_TIMER,
 | |
|                       TPL_CALLBACK,
 | |
|                       NULL,
 | |
|                       NULL,
 | |
|                       &HttpInstance->TimeoutEvent
 | |
|                       );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error2;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Get HTTP timeout value
 | |
|     //
 | |
|     TimeoutValue = PcdGet32 (PcdHttpIoTimeout);
 | |
| 
 | |
|     //
 | |
|     // Start the timer, and wait Timeout seconds to receive the body packet.
 | |
|     //
 | |
|     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, TimeoutValue * TICKS_PER_MS);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error2;
 | |
|     }
 | |
| 
 | |
|     Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
 | |
| 
 | |
|     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error2;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Process the received the body packet.
 | |
|     //
 | |
|     HttpMsg->BodyLength = MIN ((UINTN) Fragment.Len, HttpMsg->BodyLength);
 | |
| 
 | |
|     CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
 | |
| 
 | |
|     //
 | |
|     // Record the CallbackData data.
 | |
|     //
 | |
|     HttpInstance->CallbackData.Wrap = (VOID *) Wrap;
 | |
|     HttpInstance->CallbackData.ParseData = HttpMsg->Body;
 | |
|     HttpInstance->CallbackData.ParseDataLength = HttpMsg->BodyLength;
 | |
| 
 | |
|     //
 | |
|     // Parse Body with CallbackData data.
 | |
|     //
 | |
|     Status = HttpParseMessageBody (
 | |
|                HttpInstance->MsgParser,
 | |
|                HttpMsg->BodyLength,
 | |
|                HttpMsg->Body
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error2;
 | |
|     }
 | |
| 
 | |
|     if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
 | |
|       //
 | |
|       // Free the MsgParse since we already have a full HTTP message.
 | |
|       //
 | |
|       HttpFreeMsgParser (HttpInstance->MsgParser);
 | |
|       HttpInstance->MsgParser = NULL;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check whether there is the next message header in the HttpMsg->Body.
 | |
|     //
 | |
|     if (HttpInstance->NextMsg != NULL) {
 | |
|       HttpMsg->BodyLength = HttpInstance->NextMsg - (CHAR8 *) HttpMsg->Body;
 | |
|     }
 | |
| 
 | |
|     HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
 | |
|     if (HttpInstance->CacheLen != 0) {
 | |
|       if (HttpInstance->CacheBody != NULL) {
 | |
|         FreePool (HttpInstance->CacheBody);
 | |
|       }
 | |
| 
 | |
|       HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
 | |
|       if (HttpInstance->CacheBody == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto Error2;
 | |
|       }
 | |
| 
 | |
|       CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
 | |
|       HttpInstance->CacheOffset = 0;
 | |
|       if (HttpInstance->NextMsg != NULL) {
 | |
|         HttpInstance->NextMsg = HttpInstance->CacheBody;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Fragment.Bulk != NULL) {
 | |
|       FreePool (Fragment.Bulk);
 | |
|       Fragment.Bulk = NULL;
 | |
|     }
 | |
| 
 | |
|     goto Exit;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| 
 | |
| Exit:
 | |
|   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
 | |
|   if (Item != NULL) {
 | |
|     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
 | |
|     Token->Status = EFI_HTTP_ERROR;
 | |
|   } else {
 | |
|     Token->Status = Status;
 | |
|   }
 | |
| 
 | |
|   gBS->SignalEvent (Token->Event);
 | |
|   HttpCloseTcpRxEvent (Wrap);
 | |
|   FreePool (Wrap);
 | |
|   return Status;
 | |
| 
 | |
| Error2:
 | |
|   if (ValueInItem != NULL) {
 | |
|     NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
 | |
|   }
 | |
| 
 | |
| Error:
 | |
|   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
 | |
|   if (Item != NULL) {
 | |
|     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
 | |
|   }
 | |
| 
 | |
|   if (!HttpInstance->UseHttps) {
 | |
|     HttpTcpTokenCleanup (Wrap);
 | |
|   } else {
 | |
|     FreePool (Wrap);
 | |
|   }
 | |
| 
 | |
|   if (HttpHeaders != NULL) {
 | |
|     FreePool (HttpHeaders);
 | |
|     HttpHeaders = NULL;
 | |
|   }
 | |
| 
 | |
|   if (Fragment.Bulk != NULL) {
 | |
|     FreePool (Fragment.Bulk);
 | |
|     Fragment.Bulk = NULL;
 | |
|   }
 | |
| 
 | |
|   if (HttpMsg->Headers != NULL) {
 | |
|     FreePool (HttpMsg->Headers);
 | |
|     HttpMsg->Headers = NULL;
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->CacheBody != NULL) {
 | |
|     FreePool (HttpInstance->CacheBody);
 | |
|     HttpInstance->CacheBody = NULL;
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
 | |
|     Token->Status = EFI_HTTP_ERROR;
 | |
|   } else {
 | |
|     Token->Status = Status;
 | |
|   }
 | |
| 
 | |
|   gBS->SignalEvent (Token->Event);
 | |
| 
 | |
|   return Status;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The Response() function queues an HTTP response to this HTTP instance, similar to
 | |
|   Receive() function in the EFI TCP driver. When the HTTP response is received successfully,
 | |
|   or if there is an error, Status in token will be updated and Event will be signaled.
 | |
| 
 | |
|   The HTTP driver will queue a receive token to the underlying TCP instance. When data
 | |
|   is received in the underlying TCP instance, the data will be parsed and Token will
 | |
|   be populated with the response data. If the data received from the remote host
 | |
|   contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
 | |
|   (asynchronously) for more data to be sent from the remote host before signaling
 | |
|   Event in Token.
 | |
| 
 | |
|   It is the responsibility of the caller to allocate a buffer for Body and specify the
 | |
|   size in BodyLength. If the remote host provides a response that contains a content
 | |
|   body, up to BodyLength bytes will be copied from the receive buffer into Body and
 | |
|   BodyLength will be updated with the amount of bytes received and copied to Body. This
 | |
|   allows the client to download a large file in chunks instead of into one contiguous
 | |
|   block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
 | |
|   non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
 | |
|   token to underlying TCP instance. If data arrives in the receive buffer, up to
 | |
|   BodyLength bytes of data will be copied to Body. The HTTP driver will then update
 | |
|   BodyLength with the amount of bytes received and copied to Body.
 | |
| 
 | |
|   If the HTTP driver does not have an open underlying TCP connection with the host
 | |
|   specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
 | |
|   consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
 | |
|   an open TCP connection between client and host.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
|   @param[in]  Token               Pointer to storage containing HTTP response token.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Allocation succeeded.
 | |
|   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been
 | |
|                                   initialized.
 | |
|   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
 | |
|                                   This is NULL.
 | |
|                                   Token is NULL.
 | |
|                                   Token->Message->Headers is NULL.
 | |
|                                   Token->Message is NULL.
 | |
|                                   Token->Message->Body is not NULL,
 | |
|                                   Token->Message->BodyLength is non-zero, and
 | |
|                                   Token->Message->Data is NULL, but a previous call to
 | |
|                                   Response() has not been completed successfully.
 | |
|   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
 | |
|   @retval EFI_ACCESS_DENIED       An open TCP connection is not present with the host
 | |
|                                   specified by response URL.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpResponse (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This,
 | |
|   IN  EFI_HTTP_TOKEN            *Token
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   EFI_HTTP_MESSAGE              *HttpMsg;
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
|   HTTP_TOKEN_WRAP               *Wrap;
 | |
| 
 | |
|   if ((This == NULL) || (Token == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpMsg = Token->Message;
 | |
|   if (HttpMsg == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
| 
 | |
|   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether the token already existed.
 | |
|   //
 | |
|   if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {
 | |
|     return EFI_ACCESS_DENIED;
 | |
|   }
 | |
| 
 | |
|   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
 | |
|   if (Wrap == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Wrap->HttpInstance = HttpInstance;
 | |
|   Wrap->HttpToken    = Token;
 | |
| 
 | |
|   //
 | |
|   // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
 | |
|   // receive the https response. A special TlsRxToken is used for receiving TLS
 | |
|   // related messages. It should be a blocking response.
 | |
|   //
 | |
|   if (!HttpInstance->UseHttps) {
 | |
|     Status = HttpCreateTcpRxEvent (Wrap);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Error;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Error;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If already have pending RxTokens, return directly.
 | |
|   //
 | |
|   if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   return HttpResponseWorker (Wrap);
 | |
| 
 | |
| Error:
 | |
|   if (Wrap != NULL) {
 | |
|     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
 | |
|       gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
 | |
|     }
 | |
| 
 | |
|     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
 | |
|       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
 | |
|     }
 | |
|     FreePool (Wrap);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The Poll() function can be used by network drivers and applications to increase the
 | |
|   rate that data packets are moved between the communication devices and the transmit
 | |
|   and receive queues.
 | |
| 
 | |
|   In some systems, the periodic timer event in the managed network driver may not poll
 | |
|   the underlying communications device fast enough to transmit and/or receive all data
 | |
|   packets without missing incoming packets or dropping outgoing packets. Drivers and
 | |
|   applications that are experiencing packet loss should try calling the Poll() function
 | |
|   more often.
 | |
| 
 | |
|   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS             Incoming or outgoing data was processed.
 | |
|   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
 | |
|   @retval EFI_INVALID_PARAMETER   This is NULL.
 | |
|   @retval EFI_NOT_READY           No incoming or outgoing data is processed.
 | |
|   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiHttpPoll (
 | |
|   IN  EFI_HTTP_PROTOCOL         *This
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   HTTP_PROTOCOL                 *HttpInstance;
 | |
| 
 | |
|   if (This == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
 | |
| 
 | |
|   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (HttpInstance->LocalAddressIsIPv6) {
 | |
|     if (HttpInstance->Tcp6 == NULL) {
 | |
|       return EFI_NOT_STARTED;
 | |
|     }
 | |
|     Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
 | |
|   } else {
 | |
|     if (HttpInstance->Tcp4 == NULL) {
 | |
|       return EFI_NOT_STARTED;
 | |
|     }
 | |
|     Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
 | |
|   }
 | |
| 
 | |
|   DispatchDpc ();
 | |
| 
 | |
|   return Status;
 | |
| }
 |