summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c1669
1 files changed, 1669 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c
new file mode 100644
index 00000000..a1ce556e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c
@@ -0,0 +1,1669 @@
+/** @file
+ Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2015 - 2018, 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);
+ }
+
+ 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
+ );
+ 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;
+
+ 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;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ 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;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the body packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ 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;
+}