/** Cancel a TxToken or RxToken. @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; 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); // // Free resources. // NetMapRemoveItem (Map, Item, NULL); if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); } if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); } if (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { FreePool (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer); } FreePool (Wrap); // // 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; }
/** The notify function associated with RxToken for Tcp4->Receive (). @param[in] Context The context. **/ VOID EFIAPI HttpTcpReceiveNotifyDpc ( IN VOID *Context ) { HTTP_TOKEN_WRAP *Wrap; NET_MAP_ITEM *Item; UINTN Length; EFI_STATUS Status; HTTP_PROTOCOL *HttpInstance; if (Context == NULL) { return ; } Wrap = (HTTP_TOKEN_WRAP *) Context; gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); if (EFI_ERROR (Wrap->TcpWrap.RxToken.CompletionToken.Status)) { return ; } HttpInstance = Wrap->HttpInstance; // // Check whether we receive a complete HTTP message. // ASSERT (HttpInstance->MsgParser != NULL); Length = (UINTN) Wrap->TcpWrap.RxData.FragmentTable[0].FragmentLength; Status = HttpParseMessageBody ( HttpInstance->MsgParser, Length, Wrap->HttpToken->Message->Body ); if (EFI_ERROR (Status)) { return ; } if (HttpIsMessageComplete (HttpInstance->MsgParser)) { // // Free the MsgParse since we already have a full HTTP message. // HttpFreeMsgParser (HttpInstance->MsgParser); HttpInstance->MsgParser = NULL; } Wrap->HttpToken->Message->BodyLength = Length; ASSERT (HttpInstance->CacheBody == NULL); // // We receive part of header of next HTTP msg. // if (HttpInstance->NextMsg != NULL) { Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg - (CHAR8 *) Wrap->HttpToken->Message->Body; HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength; if (HttpInstance->CacheLen != 0) { HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); if (HttpInstance->CacheBody == NULL) { return ; } CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen); HttpInstance->NextMsg = HttpInstance->CacheBody; HttpInstance->CacheOffset = 0; } } Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } Wrap->TcpWrap.IsRxDone = TRUE; Wrap->HttpToken->Status = Wrap->TcpWrap.RxToken.CompletionToken.Status; gBS->SignalEvent (Wrap->HttpToken->Event); // // Check pending RxTokens and receive the HTTP message. // NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL); FreePool (Wrap); }
/** 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 opration due to lack of resources. @retval EFI_NOT_READY Can't find a corresponding TxToken. **/ EFI_STATUS HttpResponseWorker ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMsg; EFI_TCP4_IO_TOKEN *RxToken; EFI_TCP4_PROTOCOL *Tcp4; CHAR8 *EndofHeader; CHAR8 *HttpHeaders; UINTN SizeofHeaders; CHAR8 *Buffer; 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; if (Wrap == NULL || Wrap->HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } HttpInstance = Wrap->HttpInstance; Token = Wrap->HttpToken; HttpMsg = Token->Message; Tcp4 = HttpInstance->Tcp4; ASSERT (Tcp4 != NULL); HttpMsg->Headers = NULL; HttpHeaders = NULL; SizeofHeaders = 0; Buffer = NULL; BufferSize = 0; EndofHeader = NULL; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcp4RxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // 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); } RxToken = &HttpInstance->RxToken; RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } // // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. // while (EndofHeader == NULL) { HttpInstance->IsRxDone = FALSE; RxToken->Packet.RxData->DataLength = DEF_BUF_LEN; RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; Status = Tcp4->Receive (Tcp4, RxToken); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); goto Error; } while (!HttpInstance->IsRxDone) { Tcp4->Poll (Tcp4); } Status = RxToken->CompletionToken.Status; if (EFI_ERROR (Status)) { goto Error; } // // Append the response string. // BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength; Buffer = AllocateZeroPool (BufferSize); if (Buffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } if (HttpHeaders != NULL) { CopyMem (Buffer, HttpHeaders, SizeofHeaders); FreePool (HttpHeaders); } CopyMem ( Buffer + SizeofHeaders, RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer, RxToken->Packet.RxData->FragmentTable[0].FragmentLength ); HttpHeaders = Buffer; SizeofHeaders = BufferSize; // // Check whether we received end of HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); }; // // Skip the CRLF after the HTTP headers. // EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); // // 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; } FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; // // Search for Status Code. // StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; if (StatusCodeStr == NULL) { 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) { 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; // // Parse the HTTP header into array of key/value pairs. // Status = HttpUtilitiesParse (HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount); if (EFI_ERROR (Status)) { goto Error; } FreePool (HttpHeaders); HttpHeaders = NULL; HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (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 TxToken not transmitted yet, insert back and return error. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; } Status = HttpInitMsgParser ( ValueInItem->TcpWrap.Method, HttpMsg->Data.Response->StatusCode, HttpMsg->HeaderCount, HttpMsg->Headers, HttpBodyParserCallback, (VOID *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { 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 aready 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; // RxToken = &Wrap->TcpWrap.RxToken; RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; RxToken->CompletionToken.Status = EFI_NOT_READY; Status = Tcp4->Receive (Tcp4, RxToken); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); goto Error; } return Status; Exit: Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } Token->Status = Status; gBS->SignalEvent (Token->Event); FreePool (Wrap); return Status; Error2: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: if (Wrap != NULL) { if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); } RxToken = &Wrap->TcpWrap.RxToken; if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; } FreePool (Wrap); } if (HttpInstance->RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event); HttpInstance->RxToken.CompletionToken.Event = NULL; } RxToken = &HttpInstance->RxToken; if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; } if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; } Token->Status = Status; gBS->SignalEvent (Token->Event); return Status; }
/** 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 opration 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; 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; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcpRxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // 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; Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize); 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) { 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) { 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. // Status = mHttpUtilities->Parse ( mHttpUtilities, HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount ); if (EFI_ERROR (Status)) { goto Error; } 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. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; } Status = HttpInitMsgParser ( ValueInItem->TcpWrap.Method, HttpMsg->Data.Response->StatusCode, HttpMsg->HeaderCount, HttpMsg->Headers, HttpBodyParserCallback, (VOID *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { 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 aready 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; // Status = HttpTcpReceiveBody (Wrap, HttpMsg); if (EFI_ERROR (Status)) { goto Error; } 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: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: HttpTcpTokenCleanup (Wrap); if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } 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 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 opration 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; 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; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcpRxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // 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) { 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) { 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, 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) { 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. // Status = mHttpUtilities->Parse ( mHttpUtilities, HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount ); if (EFI_ERROR (Status)) { goto Error; } 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 *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { 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 aready 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); 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 body packet. // Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); if (EFI_ERROR (Status)) { goto Error; } // // We still need receive more data when there is no cache data and MsgParser is not NULL; // Status = HttpTcpReceiveBody (Wrap, HttpMsg, HttpInstance->TimeoutEvent); gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); if (EFI_ERROR (Status)) { goto Error; } FreePool (Wrap); 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: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: HttpTcpTokenCleanup (Wrap); if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } 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; }