+/** @file\r
+ Implementation of EFI_HTTP_PROTOCOL protocol interfaces.\r
+\r
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "HttpDriver.h"\r
+\r
+EFI_HTTP_PROTOCOL mEfiHttpTemplate = {\r
+ EfiHttpGetModeData,\r
+ EfiHttpConfigure,\r
+ EfiHttpRequest,\r
+ EfiHttpCancel,\r
+ EfiHttpResponse,\r
+ EfiHttpPoll\r
+};\r
+\r
+/**\r
+ Returns the operational parameters for the current HTTP child instance.\r
+\r
+ The GetModeData() function is used to read the current mode data (operational\r
+ parameters) for this HTTP protocol instance.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+ @param[out] HttpConfigData Point to buffer for operational parameters of this\r
+ HTTP instance.\r
+\r
+ @retval EFI_SUCCESS Operation succeeded.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ HttpConfigData is NULL.\r
+ HttpConfigData->AccessPoint is NULL.\r
+ @retval EFI_NOT_STARTED The HTTP instance is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpGetModeData (\r
+ IN EFI_HTTP_PROTOCOL *This,\r
+ OUT EFI_HTTP_CONFIG_DATA *HttpConfigData\r
+ )\r
+{\r
+ HTTP_PROTOCOL *HttpInstance;\r
+\r
+ if ((This == NULL) || (HttpConfigData == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL);\r
+\r
+ if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (HttpConfigData->AccessPoint.IPv4Node == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpConfigData->HttpVersion = HttpInstance->HttpVersion;\r
+ HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec;\r
+ HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;\r
+\r
+ CopyMem (\r
+ HttpConfigData->AccessPoint.IPv4Node,\r
+ &HttpInstance->IPv4Node,\r
+ sizeof (HttpInstance->IPv4Node)\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize or brutally reset the operational parameters for this EFI HTTP instance.\r
+\r
+ The Configure() function does the following:\r
+ When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring\r
+ timeout, local address, port, etc.\r
+ When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active\r
+ connections with remote hosts, canceling all asynchronous tokens, and flush request\r
+ and response buffers without informing the appropriate hosts.\r
+\r
+ Except for GetModeData() and Configure(), No other EFI HTTP function can be executed\r
+ by this instance until the Configure() function is executed and returns successfully.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+ @param[in] HttpConfigData Pointer to the configure data to configure the instance.\r
+\r
+ @retval EFI_SUCCESS Operation succeeded.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ HttpConfigData->LocalAddressIsIPv6 is FALSE and\r
+ HttpConfigData->IPv4Node is NULL.\r
+ HttpConfigData->LocalAddressIsIPv6 is TRUE and\r
+ HttpConfigData->IPv6Node is NULL.\r
+ @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling\r
+ Configure() with NULL to reset it.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when\r
+ executing Configure().\r
+ @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported\r
+ in the implementation.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpConfigure (\r
+ IN EFI_HTTP_PROTOCOL *This,\r
+ IN EFI_HTTP_CONFIG_DATA *HttpConfigData\r
+ ) \r
+{\r
+ HTTP_PROTOCOL *HttpInstance;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);\r
+\r
+ if (HttpConfigData != NULL) {\r
+ //\r
+ // Check input parameters.\r
+ //\r
+ if (HttpConfigData->LocalAddressIsIPv6) {\r
+ if (HttpConfigData->AccessPoint.IPv6Node == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ if (HttpConfigData->AccessPoint.IPv4Node == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ //\r
+ // Now configure this HTTP instance.\r
+ //\r
+ if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ HttpInstance->HttpVersion = HttpConfigData->HttpVersion;\r
+ HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec;\r
+ HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;\r
+\r
+ if (HttpConfigData->LocalAddressIsIPv6) {\r
+ return EFI_UNSUPPORTED;\r
+ } else {\r
+ CopyMem (\r
+ &HttpInstance->IPv4Node,\r
+ HttpConfigData->AccessPoint.IPv4Node,\r
+ sizeof (HttpInstance->IPv4Node)\r
+ );\r
+\r
+ HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ } else {\r
+ if (HttpInstance->LocalAddressIsIPv6) {\r
+ return EFI_UNSUPPORTED;\r
+ } else {\r
+ HttpCleanProtocol (HttpInstance);\r
+ Status = HttpInitProtocol (HttpInstance->Service, HttpInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ HttpInstance->State = HTTP_STATE_UNCONFIGED;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+}\r
+ \r
+\r
+/**\r
+ The Request() function queues an HTTP request to this HTTP instance.\r
+\r
+ Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent\r
+ successfully, or if there is an error, Status in token will be updated and Event will\r
+ be signaled.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+ @param[in] Token Pointer to storage containing HTTP request token.\r
+\r
+ @retval EFI_SUCCESS Outgoing data was processed.\r
+ @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
+ @retval EFI_UNSUPPORTED The HTTP method is not supported in current\r
+ implementation.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ Token->Message is NULL.\r
+ Token->Message->Body is not NULL,\r
+ Token->Message->BodyLength is non-zero, and\r
+ Token->Message->Data is NULL, but a previous call to\r
+ Request()has not been completed successfully.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpRequest (\r
+ IN EFI_HTTP_PROTOCOL *This,\r
+ IN EFI_HTTP_TOKEN *Token\r
+ )\r
+{\r
+ EFI_HTTP_MESSAGE *HttpMsg;\r
+ EFI_HTTP_REQUEST_DATA *Request;\r
+ VOID *UrlParser;\r
+ EFI_STATUS Status;\r
+ CHAR8 *HostName;\r
+ UINT16 RemotePort;\r
+ HTTP_PROTOCOL *HttpInstance;\r
+ BOOLEAN Configure;\r
+ BOOLEAN ReConfigure;\r
+ CHAR8 *RequestStr;\r
+ CHAR8 *Url;\r
+ CHAR16 *HostNameStr;\r
+ HTTP_TOKEN_WRAP *Wrap;\r
+ HTTP_TCP_TOKEN_WRAP *TcpWrap;\r
+\r
+ if ((This == NULL) || (Token == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpMsg = Token->Message;\r
+ if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Current implementation does not support POST/PUT method.\r
+ // If future version supports these two methods, Request could be NULL for a special case that to send large amounts\r
+ // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.\r
+ // \r
+ //\r
+ Request = HttpMsg->Data.Request;\r
+ if ((Request == NULL) || (Request->Url == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Only support GET and HEAD method in current implementation.\r
+ //\r
+ if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL);\r
+\r
+ if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (HttpInstance->LocalAddressIsIPv6) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check whether the token already existed.\r
+ //\r
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {\r
+ return EFI_ACCESS_DENIED; \r
+ } \r
+\r
+ Url = NULL;\r
+ HostName = NULL;\r
+ Wrap = NULL;\r
+ HostNameStr = NULL;\r
+ TcpWrap = NULL;\r
+\r
+ //\r
+ // Parse the URI of the remote host.\r
+ //\r
+ Url = AllocatePool (StrLen (Request->Url) + 1);\r
+ if (Url == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ UnicodeStrToAsciiStr (Request->Url, Url);\r
+ UrlParser = NULL;\r
+ Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+\r
+ RequestStr = NULL;\r
+ HostName = NULL;\r
+ Status = HttpUrlGetHostName (Url, UrlParser, &HostName);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+\r
+ Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
+ if (EFI_ERROR (Status)) {\r
+ RemotePort = HTTP_DEFAULT_PORT;\r
+ }\r
+\r
+ Configure = TRUE;\r
+ ReConfigure = TRUE; \r
+\r
+ if (HttpInstance->RemoteHost == NULL && HttpInstance->RemotePort == 0) {\r
+ //\r
+ // Request() is called the first time. \r
+ //\r
+ ReConfigure = FALSE;\r
+ } else {\r
+ if ((HttpInstance->RemotePort == RemotePort) &&\r
+ (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
+ //\r
+ // Host Name and port number of the request URL are the same with previous call to Request().\r
+ // Check whether previous TCP packet sent out.\r
+ //\r
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {\r
+ //\r
+ // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
+ //\r
+ Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
+ if (Wrap == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error1;\r
+ }\r
+\r
+ Wrap->HttpToken = Token;\r
+ Wrap->HttpInstance = HttpInstance;\r
+\r
+ Status = HttpCreateTcp4TxEvent (Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+\r
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+\r
+ Wrap->TcpWrap.Method = Request->Method;\r
+\r
+ FreePool (Url);\r
+ FreePool (HostName);\r
+ \r
+ //\r
+ // Queue the HTTP token and return.\r
+ //\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ //\r
+ // Use existing TCP instance to transmit the packet.\r
+ //\r
+ Configure = FALSE;\r
+ ReConfigure = FALSE;\r
+ }\r
+ } else {\r
+ //\r
+ // Need close existing TCP instance and create a new TCP instance for data transmit.\r
+ //\r
+ if (HttpInstance->RemoteHost != NULL) {\r
+ FreePool (HttpInstance->RemoteHost);\r
+ HttpInstance->RemoteHost = NULL;\r
+ }\r
+ }\r
+ } \r
+\r
+ if (Configure) {\r
+ //\r
+ // Parse Url for IPv4 address, if failed, perform DNS resolution.\r
+ //\r
+ Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);\r
+ if (EFI_ERROR (Status)) {\r
+ HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (UINT16));\r
+ if (HostNameStr == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error1;\r
+ }\r
+\r
+ AsciiStrToUnicodeStr (HostName, HostNameStr);\r
+ Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);\r
+ FreePool (HostNameStr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Save the RemotePort and RemoteHost.\r
+ //\r
+ ASSERT (HttpInstance->RemoteHost == NULL);\r
+ HttpInstance->RemotePort = RemotePort;\r
+ HttpInstance->RemoteHost = HostName;\r
+ HostName = NULL;\r
+ }\r
+\r
+ if (ReConfigure) {\r
+ //\r
+ // The request URL is different from previous calls to Request(), close existing TCP instance.\r
+ //\r
+ ASSERT (HttpInstance->Tcp4 != NULL);\r
+ HttpCloseConnection (HttpInstance);\r
+ EfiHttpCancel (This, NULL);\r
+ }\r
+\r
+ //\r
+ // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
+ //\r
+ Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
+ if (Wrap == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error1;\r
+ }\r
+\r
+ Wrap->HttpToken = Token;\r
+ Wrap->HttpInstance = HttpInstance;\r
+ Wrap->TcpWrap.Method = Request->Method;\r
+\r
+ if (Configure) {\r
+ //\r
+ // Configure TCP instance.\r
+ //\r
+ Status = HttpConfigureTcp4 (HttpInstance, Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+ //\r
+ // Connect TCP.\r
+ //\r
+ Status = HttpConnectTcp4 (HttpInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+ } else {\r
+ //\r
+ // For the new HTTP token, create TX TCP token events. \r
+ //\r
+ Status = HttpCreateTcp4TxEvent (Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create request message.\r
+ //\r
+ RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, Url);\r
+ if (RequestStr == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error3;\r
+ }\r
+\r
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error4;\r
+ }\r
+\r
+ FreePool (Url);\r
+ if (HostName != NULL) {\r
+ FreePool (HostName);\r
+ }\r
+\r
+ //\r
+ // Transmit the request message.\r
+ //\r
+ Status = HttpTransmitTcp4 (\r
+ HttpInstance,\r
+ Wrap,\r
+ (UINT8*) RequestStr,\r
+ AsciiStrLen (RequestStr)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error5; \r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Error5:\r
+ NetMapRemoveTail (&HttpInstance->TxTokens, NULL);\r
+\r
+Error4:\r
+ if (RequestStr != NULL) {\r
+ FreePool (RequestStr);\r
+ } \r
+\r
+Error3:\r
+ HttpCloseConnection (HttpInstance);\r
+\r
+\r
+Error2:\r
+ HttpCloseTcp4ConnCloseEvent (HttpInstance);\r
+ if (NULL != Wrap->TcpWrap.TxToken.CompletionToken.Event) {\r
+ gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event);\r
+ }\r
+\r
+Error1:\r
+ if (Url != NULL) {\r
+ FreePool (Url);\r
+ }\r
+ if (HostName != NULL) {\r
+ FreePool (HostName);\r
+ }\r
+ if (Wrap != NULL) {\r
+ FreePool (Wrap);\r
+ }\r
+ if (UrlParser!= NULL) {\r
+ HttpUrlFreeParser (UrlParser);\r
+ }\r
+\r
+ return Status;\r
+ \r
+}\r
+\r
+/**\r
+ Cancel a TxToken or RxToken. \r
+ \r
+ @param[in] Map The HTTP instance's token queue.\r
+ @param[in] Item Object container for one HTTP token and token's wrap.\r
+ @param[in] Context The user's token to cancel.\r
+\r
+ @retval EFI_SUCCESS Continue to check the next Item.\r
+ @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpCancelTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+\r
+ EFI_HTTP_TOKEN *Token;\r
+ HTTP_TOKEN_WRAP *Wrap;\r
+\r
+ Token = (EFI_HTTP_TOKEN *) Context;\r
+\r
+ //\r
+ // Return EFI_SUCCESS to check the next item in the map if\r
+ // this one doesn't match.\r
+ //\r
+ if ((Token != NULL) && (Token != Item->Key)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Wrap = (HTTP_TOKEN_WRAP *) Item->Value;\r
+ ASSERT (Wrap != NULL);\r
+\r
+ //\r
+ // Free resources.\r
+ //\r
+ NetMapRemoveItem (Map, Item, NULL); \r
+ \r
+ if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) {\r
+ gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event);\r
+ }\r
+\r
+ if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
+ gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
+ }\r
+\r
+ if (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
+ FreePool (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+ }\r
+\r
+ FreePool (Wrap);\r
+\r
+ //\r
+ // If only one item is to be cancel, return EFI_ABORTED to stop\r
+ // iterating the map any more.\r
+ //\r
+ if (Token != NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS; \r
+}\r
+\r
+/**\r
+ Cancel the user's receive/transmit request. It is the worker function of\r
+ EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the\r
+ token.\r
+\r
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.\r
+ @param[in] Token The token to cancel. If NULL, all token will be\r
+ cancelled.\r
+\r
+ @retval EFI_SUCCESS The token is cancelled.\r
+ @retval EFI_NOT_FOUND The asynchronous request or response token is not found. \r
+ @retval Others Other error as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpCancel (\r
+ IN HTTP_PROTOCOL *HttpInstance,\r
+ IN EFI_HTTP_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // First check the tokens queued by EfiHttpRequest().\r
+ //\r
+ Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Token != NULL) {\r
+ if (Status == EFI_ABORTED) {\r
+ return EFI_SUCCESS;\r
+ } \r
+ } else {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Then check the tokens queued by EfiHttpResponse().\r
+ //\r
+ Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Token != NULL) {\r
+ if (Status == EFI_ABORTED) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ } else {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Abort an asynchronous HTTP request or response token.\r
+\r
+ The Cancel() function aborts a pending HTTP request or response transaction. If\r
+ Token is not NULL and the token is in transmit or receive queues when it is being\r
+ cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will\r
+ be signaled. If the token is not in one of the queues, which usually means that the\r
+ asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,\r
+ all asynchronous tokens issued by Request() or Response() will be aborted.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+ @param[in] Token Point to storage containing HTTP request or response\r
+ token.\r
+\r
+ @retval EFI_SUCCESS Request and Response queues are successfully flushed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_STARTED This instance hasn't been configured.\r
+ @retval EFI_NO_MAPPING When using the default address, configuration (DHCP,\r
+ BOOTP, RARP, etc.) hasn't finished yet.\r
+ @retval EFI_NOT_FOUND The asynchronous request or response token is not\r
+ found.\r
+ @retval EFI_UNSUPPORTED The implementation does not support this function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpCancel (\r
+ IN EFI_HTTP_PROTOCOL *This,\r
+ IN EFI_HTTP_TOKEN *Token\r
+ )\r
+{\r
+ HTTP_PROTOCOL *HttpInstance;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL);\r
+\r
+ if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ return HttpCancel (HttpInstance, Token);\r
+\r
+}\r
+\r
+/**\r
+ A callback function to intercept events during message parser.\r
+\r
+ This function will be invoked during HttpParseMessageBody() with various events type. An error\r
+ return status of the callback function will cause the HttpParseMessageBody() aborted.\r
+\r
+ @param[in] EventType Event type of this callback call.\r
+ @param[in] Data A pointer to data buffer.\r
+ @param[in] Length Length in bytes of the Data.\r
+ @param[in] Context Callback context set by HttpInitMsgParser().\r
+\r
+ @retval EFI_SUCCESS Continue to parser the message body.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBodyParserCallback (\r
+ IN HTTP_BODY_PARSE_EVENT EventType,\r
+ IN CHAR8 *Data,\r
+ IN UINTN Length,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ HTTP_TOKEN_WRAP *Wrap;\r
+\r
+ if (EventType != BodyParseEventOnComplete) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (Data == NULL || Length != 0 || Context == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Wrap = (HTTP_TOKEN_WRAP *) Context;\r
+ Wrap->HttpInstance->NextMsg = Data;\r
+\r
+ //\r
+ // Free TxToken since already received corrsponding HTTP response.\r
+ //\r
+ FreePool (Wrap);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The work function of EfiHttpResponse().\r
+\r
+ @param[in] Wrap Pointer to HTTP token's wrap data.\r
+\r
+ @retval EFI_SUCCESS Allocation succeeded.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources.\r
+ @retval EFI_NOT_READY Can't find a corresponding TxToken.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpResponseWorker (\r
+ IN HTTP_TOKEN_WRAP *Wrap\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_MESSAGE *HttpMsg;\r
+ EFI_TCP4_IO_TOKEN *RxToken;\r
+ EFI_TCP4_PROTOCOL *Tcp4;\r
+ CHAR8 *EndofHeader;\r
+ CHAR8 *HttpHeaders;\r
+ UINTN SizeofHeaders;\r
+ CHAR8 *Buffer;\r
+ UINTN BufferSize;\r
+ UINTN StatusCode;\r
+ CHAR8 *Tmp;\r
+ CHAR8 *HeaderTmp;\r
+ CHAR8 *StatusCodeStr;\r
+ UINTN BodyLen;\r
+ HTTP_PROTOCOL *HttpInstance;\r
+ EFI_HTTP_TOKEN *Token;\r
+ NET_MAP_ITEM *Item;\r
+ HTTP_TOKEN_WRAP *ValueInItem;\r
+ UINTN HdrLen;\r
+\r
+ HttpInstance = Wrap->HttpInstance;\r
+ Token = Wrap->HttpToken;\r
+\r
+ HttpMsg = Token->Message;\r
+\r
+ Tcp4 = HttpInstance->Tcp4;\r
+ ASSERT (Tcp4 != NULL);\r
+ HttpMsg->Headers = NULL;\r
+ HttpHeaders = NULL;\r
+ SizeofHeaders = 0;\r
+ Buffer = NULL;\r
+ BufferSize = 0;\r
+ EndofHeader = NULL;\r
+ \r
+ if (HttpMsg->Data.Response != NULL) {\r
+ //\r
+ // Need receive the HTTP headers, prepare buffer.\r
+ //\r
+ Status = HttpCreateTcp4RxEventForHeader (HttpInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Check whether we have cached header from previous call.\r
+ //\r
+ if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {\r
+ //\r
+ // The data is stored at [NextMsg, CacheBody + CacheLen].\r
+ //\r
+ HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;\r
+ HttpHeaders = AllocateZeroPool (HdrLen);\r
+ if (HttpHeaders == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);\r
+ FreePool (HttpInstance->CacheBody);\r
+ HttpInstance->CacheBody = NULL;\r
+ HttpInstance->NextMsg = NULL;\r
+ HttpInstance->CacheOffset = 0;\r
+ SizeofHeaders = HdrLen;\r
+ BufferSize = HttpInstance->CacheLen;\r
+\r
+ //\r
+ // Check whether we cached the whole HTTP headers.\r
+ //\r
+ EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
+ }\r
+ \r
+ RxToken = &HttpInstance->RxToken;\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN);\r
+ if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL.\r
+ //\r
+ while (EndofHeader == NULL) { \r
+ HttpInstance->IsRxDone = FALSE;\r
+ RxToken->Packet.RxData->DataLength = DEF_BUF_LEN;\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN;\r
+ Status = Tcp4->Receive (Tcp4, RxToken);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
+ goto Error;\r
+ }\r
+ \r
+ while (!HttpInstance->IsRxDone) {\r
+ Tcp4->Poll (Tcp4);\r
+ } \r
+\r
+ Status = RxToken->CompletionToken.Status;\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Append the response string.\r
+ //\r
+ BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength;\r
+ Buffer = AllocateZeroPool (BufferSize);\r
+ if (Buffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ if (HttpHeaders != NULL) {\r
+ CopyMem (Buffer, HttpHeaders, SizeofHeaders);\r
+ FreePool (HttpHeaders);\r
+ }\r
+\r
+ CopyMem (\r
+ Buffer + SizeofHeaders,\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer,\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentLength\r
+ );\r
+ HttpHeaders = Buffer;\r
+ SizeofHeaders = BufferSize;\r
+\r
+ //\r
+ // Check whether we received end of HTTP headers.\r
+ //\r
+ EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
+ };\r
+\r
+ //\r
+ // Skip the CRLF after the HTTP headers.\r
+ //\r
+ EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR);\r
+\r
+ //\r
+ // Cache the part of body.\r
+ //\r
+ BodyLen = BufferSize - (EndofHeader - HttpHeaders);\r
+ if (BodyLen > 0) {\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ FreePool (HttpInstance->CacheBody);\r
+ }\r
+\r
+ HttpInstance->CacheBody = AllocateZeroPool (BodyLen);\r
+ if (HttpInstance->CacheBody == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);\r
+ HttpInstance->CacheLen = BodyLen;\r
+ }\r
+\r
+ FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
+\r
+ //\r
+ // Search for Status Code.\r
+ //\r
+ StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
+ if (StatusCodeStr == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);\r
+\r
+ //\r
+ // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".\r
+ //\r
+ Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);\r
+ if (Tmp == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);\r
+ SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);\r
+ HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
+ if (HeaderTmp == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (HeaderTmp, Tmp, SizeofHeaders);\r
+ FreePool (HttpHeaders);\r
+ HttpHeaders = HeaderTmp;\r
+ //\r
+ // Parse the HTTP header into array of key/value pairs.\r
+ //\r
+ Status = HttpUtilitiesParse (HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ FreePool (HttpHeaders);\r
+ HttpHeaders = NULL;\r
+ \r
+ HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);\r
+\r
+ //\r
+ // Init message-body parser by header information. \r
+ //\r
+ Status = EFI_NOT_READY;\r
+ ValueInItem = NULL;\r
+ NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);\r
+ if (ValueInItem == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // The first TxToken not transmitted yet, insert back and return error.\r
+ //\r
+ if (!ValueInItem->TcpWrap.IsTxDone) {\r
+ goto Error2;\r
+ }\r
+\r
+ Status = HttpInitMsgParser (\r
+ ValueInItem->TcpWrap.Method,\r
+ HttpMsg->Data.Response->StatusCode,\r
+ HttpMsg->HeaderCount,\r
+ HttpMsg->Headers,\r
+ HttpBodyParserCallback,\r
+ (VOID *) ValueInItem,\r
+ &HttpInstance->MsgParser\r
+ );\r
+ if (EFI_ERROR (Status)) { \r
+ goto Error2;\r
+ }\r
+\r
+ //\r
+ // Check whether we received a complete HTTP message.\r
+ //\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+\r
+ if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
+ //\r
+ // Free the MsgParse since we already have a full HTTP message.\r
+ //\r
+ HttpFreeMsgParser (HttpInstance->MsgParser);\r
+ HttpInstance->MsgParser = NULL;\r
+ }\r
+ }\r
+\r
+ if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { \r
+ Status = EFI_SUCCESS;\r
+ goto Exit;\r
+ }\r
+ } \r
+\r
+ //\r
+ // Receive the response body.\r
+ //\r
+ BodyLen = 0;\r
+\r
+ //\r
+ // First check whether we cached some data.\r
+ //\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ //\r
+ // Calculate the length of the cached data.\r
+ //\r
+ if (HttpInstance->NextMsg != NULL) {\r
+ //\r
+ // We have a cached HTTP message which includes a part of HTTP header of next message.\r
+ //\r
+ BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); \r
+ } else {\r
+ BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;\r
+ }\r
+\r
+ if (BodyLen > 0) {\r
+ //\r
+ // We have some cached data. Just copy the data and return.\r
+ //\r
+ if (HttpMsg->BodyLength < BodyLen) {\r
+ CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);\r
+ HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;\r
+ } else {\r
+ //\r
+ // Copy all cached data out.\r
+ //\r
+ CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);\r
+ HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;\r
+ HttpMsg->BodyLength = BodyLen;\r
+\r
+ if (HttpInstance->NextMsg == NULL) {\r
+ //\r
+ // There is no HTTP header of next message. Just free the cache buffer.\r
+ //\r
+ FreePool (HttpInstance->CacheBody);\r
+ HttpInstance->CacheBody = NULL;\r
+ HttpInstance->NextMsg = NULL;\r
+ HttpInstance->CacheOffset = 0;\r
+ }\r
+ }\r
+ //\r
+ // Return since we aready received required data.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ goto Exit;\r
+ } \r
+\r
+ if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {\r
+ //\r
+ // We received a complete HTTP message, and we don't have more data to return to caller.\r
+ //\r
+ HttpMsg->BodyLength = 0;\r
+ Status = EFI_SUCCESS;\r
+ goto Exit; \r
+ } \r
+ }\r
+\r
+ ASSERT (HttpInstance->MsgParser != NULL);\r
+\r
+ //\r
+ // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
+ //\r
+ RxToken = &Wrap->TcpWrap.RxToken;\r
+\r
+ RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength;\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength;\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body;\r
+\r
+ RxToken->CompletionToken.Status = EFI_NOT_READY;\r
+ Status = Tcp4->Receive (Tcp4, RxToken);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
+ goto Error;\r
+ }\r
+\r
+ return Status;\r
+\r
+Exit:\r
+ Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);\r
+ if (Item != NULL) {\r
+ NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
+ }\r
+ Token->Status = Status;\r
+ gBS->SignalEvent (Token->Event);\r
+ FreePool (Wrap);\r
+ return Status;\r
+\r
+Error2:\r
+ NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
+\r
+Error:\r
+ if (Wrap != NULL) {\r
+ if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
+ gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
+ }\r
+ RxToken = &Wrap->TcpWrap.RxToken;\r
+ if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
+ FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
+ }\r
+ FreePool (Wrap);\r
+ }\r
+\r
+ if (HttpInstance->RxToken.CompletionToken.Event != NULL) {\r
+ gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event);\r
+ HttpInstance->RxToken.CompletionToken.Event = NULL;\r
+ }\r
+\r
+ RxToken = &HttpInstance->RxToken;\r
+ if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
+ FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+ RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;\r
+ }\r
+ \r
+ if (HttpHeaders != NULL) {\r
+ FreePool (HttpHeaders);\r
+ }\r
+\r
+ if (HttpMsg->Headers != NULL) {\r
+ FreePool (HttpMsg->Headers);\r
+ }\r
+\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ FreePool (HttpInstance->CacheBody);\r
+ HttpInstance->CacheBody = NULL;\r
+ }\r
+\r
+ Token->Status = Status;\r
+ gBS->SignalEvent (Token->Event);\r
+\r
+ return Status; \r
+\r
+}\r
+\r
+\r
+/**\r
+ The Response() function queues an HTTP response to this HTTP instance, similar to\r
+ Receive() function in the EFI TCP driver. When the HTTP request is sent successfully,\r
+ or if there is an error, Status in token will be updated and Event will be signaled.\r
+\r
+ The HTTP driver will queue a receive token to the underlying TCP instance. When data\r
+ is received in the underlying TCP instance, the data will be parsed and Token will\r
+ be populated with the response data. If the data received from the remote host\r
+ contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting\r
+ (asynchronously) for more data to be sent from the remote host before signaling\r
+ Event in Token.\r
+\r
+ It is the responsibility of the caller to allocate a buffer for Body and specify the\r
+ size in BodyLength. If the remote host provides a response that contains a content\r
+ body, up to BodyLength bytes will be copied from the receive buffer into Body and\r
+ BodyLength will be updated with the amount of bytes received and copied to Body. This\r
+ allows the client to download a large file in chunks instead of into one contiguous\r
+ block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is\r
+ non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive\r
+ token to underlying TCP instance. If data arrives in the receive buffer, up to\r
+ BodyLength bytes of data will be copied to Body. The HTTP driver will then update\r
+ BodyLength with the amount of bytes received and copied to Body.\r
+\r
+ If the HTTP driver does not have an open underlying TCP connection with the host\r
+ specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is\r
+ consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain\r
+ an open TCP connection between client and host.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+ @param[in] Token Pointer to storage containing HTTP response token.\r
+\r
+ @retval EFI_SUCCESS Allocation succeeded.\r
+ @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been\r
+ initialized.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ Token is NULL.\r
+ Token->Message->Headers is NULL.\r
+ Token->Message is NULL.\r
+ Token->Message->Body is not NULL,\r
+ Token->Message->BodyLength is non-zero, and\r
+ Token->Message->Data is NULL, but a previous call to\r
+ Response() has not been completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
+ @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host\r
+ specified by response URL.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpResponse (\r
+ IN EFI_HTTP_PROTOCOL *This,\r
+ IN EFI_HTTP_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HTTP_MESSAGE *HttpMsg;\r
+ HTTP_PROTOCOL *HttpInstance;\r
+ HTTP_TOKEN_WRAP *Wrap;\r
+\r
+ if ((This == NULL) || (Token == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpMsg = Token->Message;\r
+ if (HttpMsg == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL);\r
+\r
+ if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (HttpInstance->LocalAddressIsIPv6) {\r
+ return EFI_UNSUPPORTED;\r
+ } \r
+\r
+ //\r
+ // Check whether the token already existed.\r
+ //\r
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {\r
+ return EFI_ACCESS_DENIED; \r
+ }\r
+\r
+ Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
+ if (Wrap == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Wrap->HttpInstance = HttpInstance;\r
+ Wrap->HttpToken = Token;\r
+\r
+ Status = HttpCreateTcp4RxEvent (Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // If already have pending RxTokens, return directly.\r
+ //\r
+ if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return HttpResponseWorker (Wrap);\r
+\r
+Error:\r
+ if (Wrap != NULL) {\r
+ if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {\r
+ gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);\r
+ }\r
+ FreePool (Wrap);\r
+ } \r
+\r
+ return Status; \r
+}\r
+\r
+/**\r
+ The Poll() function can be used by network drivers and applications to increase the\r
+ rate that data packets are moved between the communication devices and the transmit\r
+ and receive queues.\r
+\r
+ In some systems, the periodic timer event in the managed network driver may not poll\r
+ the underlying communications device fast enough to transmit and/or receive all data\r
+ packets without missing incoming packets or dropping outgoing packets. Drivers and\r
+ applications that are experiencing packet loss should try calling the Poll() function\r
+ more often.\r
+\r
+ @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.\r
+ @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiHttpPoll (\r
+ IN EFI_HTTP_PROTOCOL *This\r
+ )\r
+{\r
+ HTTP_PROTOCOL *HttpInstance;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
+ ASSERT (HttpInstance != NULL);\r
+\r
+ if (HttpInstance->LocalAddressIsIPv6) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (HttpInstance->Tcp4 == NULL || HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ return HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);\r
+}\r