- Use HTTP instance as the parameter for TlsCreateChild function. - Install TLS protocol on the HTTP instance thats create TLS child. Signed-off-by: Abner Chang <abner.chang@amd.com> Cc: Saloni Kasbekar <saloni.kasbekar@intel.com> Cc: Zachary Clark-williams <zachary.clark-williams@intel.com> Cc: Michael Brown <mcb30@ipxe.org> Cc: Nickle Wang <nicklew@nvidia.com> Cc: Igor Kulchytskyy <igork@ami.com> Reviewed-by: Michael Brown <mcb30@ipxe.org> Reviewed-by: Saloni Kasbekar <saloni.kasbekar@intel.com>
		
			
				
	
	
		
			1696 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1696 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>
 | 
						|
  Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.<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;
 | 
						|
    HttpInstance->ConnectionClose    = FALSE;
 | 
						|
 | 
						|
    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;
 | 
						|
 | 
						|
  //
 | 
						|
  // 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 ((DEBUG_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->TlsAlreadyCreated) {
 | 
						|
      // Create TLS child for this HTTP instance.
 | 
						|
      Status = TlsCreateChild (HttpInstance);
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        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->ConnectionClose == FALSE) &&
 | 
						|
          (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 ((DEBUG_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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  HttpInstance->ConnectionClose = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // 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;
 | 
						|
  UINTN             Index;
 | 
						|
 | 
						|
  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;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Check server's HTTP version.
 | 
						|
    //
 | 
						|
    if (AsciiStrnCmp (HttpHeaders, "HTTP/1.0", sizeof ("HTTP/1.0") - 1) == 0) {
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "HTTP: Server version is 1.0. Setting Connection close.\n"));
 | 
						|
      HttpInstance->ConnectionClose = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // 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;
 | 
						|
 | 
						|
      for (Index = 0; Index < HttpMsg->HeaderCount; ++Index) {
 | 
						|
        if ((AsciiStriCmp ("Connection", HttpMsg->Headers[Index].FieldName) == 0) &&
 | 
						|
            (AsciiStriCmp ("close", HttpMsg->Headers[Index].FieldValue) == 0))
 | 
						|
        {
 | 
						|
          DEBUG ((DEBUG_VERBOSE, "Http: 'Connection: close' header received.\n"));
 | 
						|
          HttpInstance->ConnectionClose = TRUE;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // 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;
 | 
						|
}
 |