X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=NetworkPkg%2FHttpDxe%2FHttpImpl.c;h=17deceb395473e7457e2c3616c7059eea423cc56;hp=b46f0ae3caef3a848ae5f0259cc72a1de1f54deb;hb=895b87e38015e0698c6a5c0633e0156b038a56f1;hpb=f0ab5a81b46b9bf0a8a4234782e70cb941d91f47 diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c index b46f0ae3ca..17deceb395 100644 --- a/NetworkPkg/HttpDxe/HttpImpl.c +++ b/NetworkPkg/HttpDxe/HttpImpl.c @@ -1,8 +1,8 @@ /** @file Implementation of EFI_HTTP_PROTOCOL protocol interfaces. - Copyright (c) 2016, Intel Corporation. All rights reserved.
- (C) Copyright 2015 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+ (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -33,16 +33,18 @@ EFI_HTTP_PROTOCOL mEfiHttpTemplate = { @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. @param[out] HttpConfigData Point to buffer for operational parameters of this - HTTP instance. + 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. - HttpInstance->LocalAddressIsIPv6 is FALSE and - HttpConfigData->IPv4Node is NULL. - HttpInstance->LocalAddressIsIPv6 is TRUE and - HttpConfigData->IPv6Node is NULL. + HttpConfigData->AccessPoint.IPv4Node or + HttpConfigData->AccessPoint.IPv6Node is NULL. @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. **/ @@ -63,10 +65,9 @@ EfiHttpGetModeData ( } HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); - ASSERT (HttpInstance != NULL); - if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) || - (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) { + if ((HttpConfigData->AccessPoint.IPv6Node == NULL) || + (HttpConfigData->AccessPoint.IPv4Node == NULL)) { return EFI_INVALID_PARAMETER; } @@ -114,11 +115,10 @@ EfiHttpGetModeData ( @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->LocalAddressIsIPv6 is FALSE and - HttpConfigData->IPv4Node is NULL. + HttpConfigData->AccessPoint.IPv4Node is NULL. HttpConfigData->LocalAddressIsIPv6 is TRUE and - HttpConfigData->IPv6Node is NULL. + 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. @@ -131,27 +131,31 @@ EFI_STATUS EFIAPI EfiHttpConfigure ( IN EFI_HTTP_PROTOCOL *This, - IN EFI_HTTP_CONFIG_DATA *HttpConfigData - ) + 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))) { + (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 != NULL && HttpInstance->Service != NULL); + ASSERT (HttpInstance->Service != NULL); if (HttpConfigData != NULL) { + if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) { + return EFI_UNSUPPORTED; + } + // // Now configure this HTTP instance. // @@ -162,8 +166,8 @@ EfiHttpConfigure ( HttpInstance->HttpVersion = HttpConfigData->HttpVersion; HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec; HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6; - - if (HttpConfigData->LocalAddressIsIPv6) { + + if (HttpConfigData->LocalAddressIsIPv6) { CopyMem ( &HttpInstance->Ipv6Node, HttpConfigData->AccessPoint.IPv6Node, @@ -176,6 +180,7 @@ EfiHttpConfigure ( sizeof (HttpInstance->IPv4Node) ); } + // // Creat Tcp child // @@ -183,7 +188,7 @@ EfiHttpConfigure ( if (EFI_ERROR (Status)) { return Status; } - + HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; return EFI_SUCCESS; @@ -196,7 +201,7 @@ EfiHttpConfigure ( return EFI_SUCCESS; } } - + /** The Request() function queues an HTTP request to this HTTP instance. @@ -236,164 +241,261 @@ EfiHttpRequest ( VOID *UrlParser; EFI_STATUS Status; CHAR8 *HostName; + UINTN HostNameSize; UINT16 RemotePort; HTTP_PROTOCOL *HttpInstance; BOOLEAN Configure; BOOLEAN ReConfigure; - CHAR8 *RequestStr; + 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) || (HttpMsg->Headers == NULL)) { + if (HttpMsg == NULL) { return EFI_INVALID_PARAMETER; } - // - // Current implementation does not support POST/PUT method. - // If future version supports these two methods, Request could be NULL for a special case that to send large amounts - // of data. For this case, the implementation need check whether previous call to Request() has been completed or not. - // - // Request = HttpMsg->Data.Request; - if ((Request == NULL) || (Request->Url == NULL)) { - return EFI_INVALID_PARAMETER; - } // - // Only support GET and HEAD method in current implementation. + // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation. // - if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) { + 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); - ASSERT (HttpInstance != NULL); + + // + // Capture the method into HttpInstance. + // + if (Request != NULL) { + HttpInstance->Method = Request->Method; + } if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { return EFI_NOT_STARTED; } - // - // Check whether the token already existed. - // - if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) { - return EFI_ACCESS_DENIED; - } + 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; + } - HostName = NULL; - Wrap = NULL; - HostNameStr = NULL; + // + // 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; + } - // - // 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; + // + // 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; } - FreePool (HttpInstance->Url); - HttpInstance->Url = Url; - } + // + // 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; + } - UnicodeStrToAsciiStr (Request->Url, Url); - UrlParser = NULL; - Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); - if (EFI_ERROR (Status)) { - goto Error1; - } + // + // 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; + } - RequestStr = NULL; - HostName = NULL; - Status = HttpUrlGetHostName (Url, UrlParser, &HostName); - if (EFI_ERROR (Status)) { - goto Error1; - } - Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); - if (EFI_ERROR (Status)) { - 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; + UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen); - if (HttpInstance->RemoteHost == NULL) { // - // Request() is called the first time. + // From the information in Url, the HTTP instance will + // be able to determine whether to use http or https. // - ReConfigure = FALSE; - } else { - if ((HttpInstance->RemotePort == RemotePort) && - (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) { + 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) { // - // Host Name and port number of the request URL are the same with previous call to Request(). - // Check whether previous TCP packet sent out. + // Use TlsSb to create Tls child and open the TLS protocol. // - 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; - } + if (HttpInstance->LocalAddressIsIPv6) { + ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle; + } else { + ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle; + } - Wrap->HttpToken = Token; - Wrap->HttpInstance = HttpInstance; + HttpInstance->TlsChildHandle = TlsCreateChild ( + ImageHandle, + &(HttpInstance->TlsSb), + &(HttpInstance->Tls), + &(HttpInstance->TlsConfiguration) + ); + if (HttpInstance->TlsChildHandle == NULL) { + return EFI_DEVICE_ERROR; + } - Status = HttpCreateTcpTxEvent (Wrap); - if (EFI_ERROR (Status)) { - goto Error1; - } + TlsConfigure = TRUE; + } - Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); - if (EFI_ERROR (Status)) { - goto Error1; - } + UrlParser = NULL; + Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); + if (EFI_ERROR (Status)) { + goto Error1; + } - Wrap->TcpWrap.Method = Request->Method; + HostName = NULL; + Status = HttpUrlGetHostName (Url, UrlParser, &HostName); + if (EFI_ERROR (Status)) { + goto Error1; + } - FreePool (HostName); - + 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))) { // - // Queue the HTTP token and return. + // 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. // - return EFI_SUCCESS; + + 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 { // - // Use existing TCP instance to transmit the packet. + // Need close existing TCP instance and create a new TCP instance for data transmit. // - 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 (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + HttpInstance->RemotePort = 0; + } } } - } + } if (Configure) { // @@ -406,26 +508,27 @@ EfiHttpRequest ( } if (EFI_ERROR (Status)) { - HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16)); + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); if (HostNameStr == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error1; } - - AsciiStrToUnicodeStr (HostName, HostNameStr); + + 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. // @@ -444,6 +547,16 @@ EfiHttpRequest ( } 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); } @@ -459,28 +572,35 @@ EfiHttpRequest ( Wrap->HttpToken = Token; Wrap->HttpInstance = HttpInstance; - Wrap->TcpWrap.Method = Request->Method; + if (Request != NULL) { + Wrap->TcpWrap.Method = Request->Method; + } - Status = HttpInitTcp (HttpInstance, Wrap, Configure); + Status = HttpInitSession ( + HttpInstance, + Wrap, + Configure || ReConfigure, + TlsConfigure + ); if (EFI_ERROR (Status)) { goto Error2; - } + } - if (!Configure) { + if (!Configure && !ReConfigure && !TlsConfigure) { // - // For the new HTTP token, create TX TCP token events. + // 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 (*FileUrl != '/') { + if (Url != NULL && *FileUrl != '/') { // // Convert the absolute-URI to the absolute-path // @@ -497,15 +617,24 @@ EfiHttpRequest ( goto Error3; } } - RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, FileUrl); - if (RequestStr == NULL) { - Status = EFI_OUT_OF_RESOURCES; + + Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize); + + if (EFI_ERROR (Status) || NULL == RequestMsg) { goto Error3; } - Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); - if (EFI_ERROR (Status)) { - goto Error4; + // + // 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; + } } // @@ -514,33 +643,48 @@ EfiHttpRequest ( Status = HttpTransmitTcp ( HttpInstance, Wrap, - (UINT8*) RequestStr, - AsciiStrLen (RequestStr) + (UINT8*) RequestMsg, + RequestMsgSize ); if (EFI_ERROR (Status)) { - goto Error5; + 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 (RequestStr != NULL) { - FreePool (RequestStr); - } + if (RequestMsg != NULL) { + FreePool (RequestMsg); + } Error3: - HttpCloseConnection (HttpInstance); + 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); @@ -552,24 +696,23 @@ Error2: } Error1: - if (HostName != NULL) { FreePool (HostName); } if (Wrap != NULL) { FreePool (Wrap); } - if (UrlParser!= NULL) { + if (UrlParser != NULL) { HttpUrlFreeParser (UrlParser); } return Status; - + } /** - Cancel a user's Token. - + 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. @@ -586,7 +729,6 @@ HttpCancelTokens ( IN VOID *Context ) { - EFI_HTTP_TOKEN *Token; HTTP_TOKEN_WRAP *Wrap; HTTP_PROTOCOL *HttpInstance; @@ -605,41 +747,32 @@ HttpCancelTokens ( ASSERT (Wrap != NULL); HttpInstance = Wrap->HttpInstance; - // - // Free resources. - // - NetMapRemoveItem (Map, Item, NULL); - if (!HttpInstance->LocalAddressIsIPv6) { - if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); - } - if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); - } - - if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer); - } + // + // Cancle the Token before close its Event. + // + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken); - } else { - if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); } - + } else { if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); - } + // + // Cancle the Token before close its Event. + // + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken); - if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer); + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); } } - - FreePool (Wrap); - // // If only one item is to be cancel, return EFI_ABORTED to stop // iterating the map any more. @@ -648,7 +781,7 @@ HttpCancelTokens ( return EFI_ABORTED; } - return EFI_SUCCESS; + return EFI_SUCCESS; } /** @@ -661,7 +794,7 @@ HttpCancelTokens ( cancelled. @retval EFI_SUCCESS The token is cancelled. - @retval EFI_NOT_FOUND The asynchronous request or response token is not found. + @retval EFI_NOT_FOUND The asynchronous request or response token is not found. @retval Others Other error as indicated. **/ @@ -681,25 +814,33 @@ HttpCancel ( if (Token != NULL) { if (Status == EFI_ABORTED) { return EFI_SUCCESS; - } + } } else { return Status; } } - // - // Then check the tokens queued by EfiHttpResponse(). - // - Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); - if (EFI_ERROR (Status)) { - if (Token != NULL) { - if (Status == EFI_ABORTED) { - return EFI_SUCCESS; + 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 EFI_NOT_FOUND; + return Status; } + } + } else { + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); } else { - return Status; + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); } } @@ -743,7 +884,6 @@ EfiHttpCancel ( } HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); - ASSERT (HttpInstance != NULL); if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { return EFI_NOT_STARTED; @@ -776,6 +916,7 @@ HttpBodyParserCallback ( IN VOID *Context ) { + HTTP_CALLBACK_DATA *CallbackData; HTTP_TOKEN_WRAP *Wrap; UINTN BodyLength; CHAR8 *Body; @@ -788,20 +929,17 @@ HttpBodyParserCallback ( return EFI_SUCCESS; } - Wrap = (HTTP_TOKEN_WRAP *) Context; - Body = Wrap->HttpToken->Message->Body; - BodyLength = Wrap->HttpToken->Message->BodyLength; + 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; } - - - // - // Free Tx4Token or Tx6Token since already received corrsponding HTTP response. - // - FreePool (Wrap); return EFI_SUCCESS; } @@ -813,7 +951,7 @@ HttpBodyParserCallback ( @retval EFI_SUCCESS Allocation succeeded. @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. - @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or + @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or the EFI_HTTP_UTILITIES_PROTOCOL is not available. **/ @@ -838,11 +976,12 @@ HttpResponseWorker ( 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; @@ -854,16 +993,11 @@ HttpResponseWorker ( SizeofHeaders = 0; BufferSize = 0; EndofHeader = NULL; - - if (HttpMsg->Data.Response != NULL) { - // - // Need receive the HTTP headers, prepare buffer. - // - Status = HttpCreateTcpRxEventForHeader (HttpInstance); - if (EFI_ERROR (Status)) { - goto Error; - } + ValueInItem = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; + if (HttpMsg->Data.Response != NULL) { // // Check whether we have cached header from previous call. // @@ -889,13 +1023,41 @@ HttpResponseWorker ( // // Check whether we cached the whole HTTP headers. // - EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); - } + EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); + } HttpInstance->EndofHeader = &EndofHeader; HttpInstance->HttpHeaders = &HttpHeaders; - Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize); + + 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; } @@ -926,6 +1088,7 @@ HttpResponseWorker ( // StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; if (StatusCodeStr == NULL) { + Status = EFI_NOT_READY; goto Error; } @@ -936,86 +1099,123 @@ HttpResponseWorker ( // Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); if (Tmp == NULL) { - goto Error; - } - - Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); - SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); - HeaderTmp = AllocateZeroPool (SizeofHeaders); - if (HeaderTmp == NULL) { - goto Error; - } - - 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 Error; } - + // - // Parse the HTTP header into array of key/value pairs. + // 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. // - Status = mHttpUtilities->Parse ( - mHttpUtilities, - HttpHeaders, - SizeofHeaders, - &HttpMsg->Headers, - &HttpMsg->HeaderCount - ); - if (EFI_ERROR (Status)) { - goto Error; + Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); + if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) { + SizeofHeaders = 0; + } else { + SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); } - FreePool (HttpHeaders); - HttpHeaders = NULL; - HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); + HttpInstance->StatusCode = StatusCode; - // - // Init message-body parser by header information. - // Status = EFI_NOT_READY; ValueInItem = NULL; - NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); - if (ValueInItem == NULL) { - goto Error; - } // - // The first Tx Token not transmitted yet, insert back and return error. + // 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 (!ValueInItem->TcpWrap.IsTxDone) { - goto Error2; - } + if (!NetMapIsEmpty (&HttpInstance->TxTokens)) { + NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); + if (ValueInItem == NULL) { + goto Error; + } - Status = HttpInitMsgParser ( - ValueInItem->TcpWrap.Method, - HttpMsg->Data.Response->StatusCode, - HttpMsg->HeaderCount, - HttpMsg->Headers, - HttpBodyParserCallback, - (VOID *) ValueInItem, - &HttpInstance->MsgParser - ); - if (EFI_ERROR (Status)) { - goto Error2; + // + // The first Tx Token not transmitted yet, insert back and return error. + // + if (!ValueInItem->TcpWrap.IsTxDone) { + goto Error2; + } } - // - // Check whether we received a complete HTTP message. - // - if (HttpInstance->CacheBody != NULL) { - Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); + 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. @@ -1025,11 +1225,11 @@ HttpResponseWorker ( } } - if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { + if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { Status = EFI_SUCCESS; goto Exit; } - } + } // // Receive the response body. @@ -1047,7 +1247,7 @@ HttpResponseWorker ( // // We have a cached HTTP message which includes a part of HTTP header of next message. // - BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); + BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); } else { BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; } @@ -1082,7 +1282,7 @@ HttpResponseWorker ( // Status = EFI_SUCCESS; goto Exit; - } + } if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { // @@ -1090,8 +1290,8 @@ HttpResponseWorker ( // HttpMsg->BodyLength = 0; Status = EFI_SUCCESS; - goto Exit; - } + goto Exit; + } } ASSERT (HttpInstance->MsgParser != NULL); @@ -1099,9 +1299,112 @@ HttpResponseWorker ( // // We still need receive more data when there is no cache data and MsgParser is not NULL; // - Status = HttpTcpReceiveBody (Wrap, HttpMsg); - if (EFI_ERROR (Status)) { - goto Error; + 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 (Fragment.Len, (UINT32) 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; @@ -1111,24 +1414,48 @@ Exit: if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } - Token->Status = Status; + + 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: - NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); + if (ValueInItem != NULL) { + NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); + } Error: - HttpTcpTokenCleanup (Wrap); - + 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) { @@ -1136,10 +1463,15 @@ Error: HttpInstance->CacheBody = NULL; } - Token->Status = Status; + 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; + return Status; } @@ -1211,9 +1543,8 @@ EfiHttpResponse ( if (HttpMsg == NULL) { return EFI_INVALID_PARAMETER; } - + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); - ASSERT (HttpInstance != NULL); if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { return EFI_NOT_STARTED; @@ -1223,7 +1554,7 @@ EfiHttpResponse ( // Check whether the token already existed. // if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) { - return EFI_ACCESS_DENIED; + return EFI_ACCESS_DENIED; } Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); @@ -1234,9 +1565,16 @@ EfiHttpResponse ( Wrap->HttpInstance = HttpInstance; Wrap->HttpToken = Token; - Status = HttpCreateTcpRxEvent (Wrap); - if (EFI_ERROR (Status)) { - goto Error; + // + // 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); @@ -1263,9 +1601,9 @@ Error: gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); } FreePool (Wrap); - } + } - return Status; + return Status; } /** @@ -1302,12 +1640,11 @@ EfiHttpPoll ( } HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); - ASSERT (HttpInstance != NULL); if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { return EFI_NOT_STARTED; } - + if (HttpInstance->LocalAddressIsIPv6) { if (HttpInstance->Tcp6 == NULL) { return EFI_NOT_STARTED; @@ -1319,8 +1656,8 @@ EfiHttpPoll ( } Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); } - + DispatchDpc (); - + return Status; }