From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c | 854 +++++++++++++++++++++ 1 file changed, 854 insertions(+) create mode 100644 src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c') diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c new file mode 100644 index 00000000..b03e5858 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c @@ -0,0 +1,854 @@ +/** @file + Http IO Helper Library. + + (C) Copyright 2020 Hewlett-Packard Development Company, L.P.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + Notify the callback function when an event is triggered. + + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpIoNotifyDpc ( + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The event signaled. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpIoNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context); +} + +/** + Destroy the HTTP_IO and release the resources. + + @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. + +**/ +VOID +HttpIoDestroyIo ( + IN HTTP_IO *HttpIo + ) +{ + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if (HttpIo == NULL) { + return; + } + + Event = HttpIo->ReqToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Event = HttpIo->RspToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Event = HttpIo->TimeoutEvent; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Http = HttpIo->Http; + if (Http != NULL) { + Http->Configure (Http, NULL); + gBS->CloseProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + HttpIo->Image, + HttpIo->Controller + ); + } + + NetLibDestroyServiceChild ( + HttpIo->Controller, + HttpIo->Image, + &gEfiHttpServiceBindingProtocolGuid, + HttpIo->Handle + ); +} + +/** + Create a HTTP_IO to access the HTTP service. It will create and configure + a HTTP child handle. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + @param[in] ConfigData The HTTP_IO configuration data , + NULL means not to configure the HTTP child. + @param[in] Callback Callback function which will be invoked when specified + HTTP_IO_CALLBACK_EVENT happened. + @param[in] Context The Context data which will be passed to the Callback function. + @param[out] HttpIo The HTTP_IO. + + @retval EFI_SUCCESS The HTTP_IO is created and configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Failed to create the HTTP_IO or configure it. + +**/ +EFI_STATUS +HttpIoCreateIo ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion, + IN HTTP_IO_CONFIG_DATA *ConfigData, OPTIONAL + IN HTTP_IO_CALLBACK Callback, + IN VOID *Context, + OUT HTTP_IO *HttpIo + ) +{ + EFI_STATUS Status; + EFI_HTTP_CONFIG_DATA HttpConfigData; + EFI_HTTPv4_ACCESS_POINT Http4AccessPoint; + EFI_HTTPv6_ACCESS_POINT Http6AccessPoint; + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) { + return EFI_UNSUPPORTED; + } + + ZeroMem (HttpIo, sizeof (HTTP_IO)); + ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA)); + + // + // Create the HTTP child instance and get the HTTP protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiHttpServiceBindingProtocolGuid, + &HttpIo->Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) || (Http == NULL)) { + goto ON_ERROR; + } + + // + // Init the configuration data and configure the HTTP child. + // + HttpIo->Image = Image; + HttpIo->Controller = Controller; + HttpIo->IpVersion = IpVersion; + HttpIo->Http = Http; + HttpIo->Callback = Callback; + HttpIo->Context = Context; + HttpIo->Timeout = PcdGet32 (PcdHttpIoTimeout); + + if (ConfigData != NULL) { + if (HttpIo->IpVersion == IP_VERSION_4) { + HttpConfigData.LocalAddressIsIPv6 = FALSE; + HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion; + HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut; + + Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress; + Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort; + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp); + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask); + HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; + } else { + HttpConfigData.LocalAddressIsIPv6 = TRUE; + HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion; + HttpConfigData.TimeOutMillisec = ConfigData->Config6.RequestTimeOut; + + Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort; + IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp); + HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint; + } + + Status = Http->Configure (Http, &HttpConfigData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpIoNotify, + &HttpIo->IsTxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->ReqToken.Event = Event; + HttpIo->ReqToken.Message = &HttpIo->ReqMessage; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpIoNotify, + &HttpIo->IsRxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->RspToken.Event = Event; + HttpIo->RspToken.Message = &HttpIo->RspMessage; + + // + // Create TimeoutEvent for response + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->TimeoutEvent = Event; + return EFI_SUCCESS; + +ON_ERROR: + HttpIoDestroyIo (HttpIo); + + return Status; +} + +/** + Synchronously send a HTTP REQUEST message to the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] Request A pointer to storage such data as URL and HTTP method. + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] BodyLength Length in bytes of the HTTP body. + @param[in] Body Body associated with the HTTP request. + + @retval EFI_SUCCESS The HTTP request is trasmitted. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoSendRequest ( + IN HTTP_IO *HttpIo, + IN EFI_HTTP_REQUEST_DATA *Request, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + IN UINTN BodyLength, + IN VOID *Body + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + + if (HttpIo == NULL || HttpIo->Http == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpIo->ReqToken.Status = EFI_NOT_READY; + HttpIo->ReqToken.Message->Data.Request = Request; + HttpIo->ReqToken.Message->HeaderCount = HeaderCount; + HttpIo->ReqToken.Message->Headers = Headers; + HttpIo->ReqToken.Message->BodyLength = BodyLength; + HttpIo->ReqToken.Message->Body = Body; + + if (HttpIo->Callback != NULL) { + Status = HttpIo->Callback ( + HttpIoRequest, + HttpIo->ReqToken.Message, + HttpIo->Context + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Queue the request token to HTTP instances. + // + Http = HttpIo->Http; + HttpIo->IsTxDone = FALSE; + Status = Http->Request ( + Http, + &HttpIo->ReqToken + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the network until transmit finish. + // + while (!HttpIo->IsTxDone) { + Http->Poll (Http); + } + + return HttpIo->ReqToken.Status; +} + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). + FALSE to continue receive the previous response message. + @param[out] ResponseData Point to a wrapper of the received response data. + + @retval EFI_SUCCESS The HTTP response is received. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoRecvResponse ( + IN HTTP_IO *HttpIo, + IN BOOLEAN RecvMsgHeader, + OUT HTTP_IO_RESPONSE_DATA *ResponseData + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + + if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Queue the response token to HTTP instances. + // + HttpIo->RspToken.Status = EFI_NOT_READY; + if (RecvMsgHeader) { + HttpIo->RspToken.Message->Data.Response = &ResponseData->Response; + } else { + HttpIo->RspToken.Message->Data.Response = NULL; + } + HttpIo->RspToken.Message->HeaderCount = 0; + HttpIo->RspToken.Message->Headers = NULL; + HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength; + HttpIo->RspToken.Message->Body = ResponseData->Body; + + Http = HttpIo->Http; + HttpIo->IsRxDone = FALSE; + + // + // Start the timer, and wait Timeout seconds to receive the header packet. + // + Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Http->Response ( + Http, + &HttpIo->RspToken + ); + + if (EFI_ERROR (Status)) { + // + // Remove timeout timer from the event list. + // + gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0); + return Status; + } + + // + // Poll the network until receive finish. + // + while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent))) { + Http->Poll (Http); + } + + // + // Remove timeout timer from the event list. + // + gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0); + + if (!HttpIo->IsRxDone) { + // + // Timeout occurs, cancel the response token. + // + Http->Cancel (Http, &HttpIo->RspToken); + + Status = EFI_TIMEOUT; + + return Status; + } else { + HttpIo->IsRxDone = FALSE; + } + + if ((HttpIo->Callback != NULL) && + (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) { + Status = HttpIo->Callback ( + HttpIoResponse, + HttpIo->RspToken.Message, + HttpIo->Context + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Store the received data into the wrapper. + // + ResponseData->Status = HttpIo->RspToken.Status; + ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount; + ResponseData->Headers = HttpIo->RspToken.Message->Headers; + ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength; + + return Status; +} + +/** + Get the value of the content length if there is a "Content-Length" header. + + @param[in] HeaderCount Number of HTTP header structures in Headers. + @param[in] Headers Array containing list of HTTP headers. + @param[out] ContentLength Pointer to save the value of the content length. + + @retval EFI_SUCCESS Successfully get the content length. + @retval EFI_NOT_FOUND No "Content-Length" header in the Headers. + +**/ +EFI_STATUS +HttpIoGetContentLength ( + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + OUT UINTN *ContentLength + ) +{ + EFI_HTTP_HEADER *Header; + + Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH); + if (Header == NULL) { + return EFI_NOT_FOUND; + } + + return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength); +} +/** + Send HTTP request in chunks. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] SendChunkProcess Pointer to current chunk process status. + @param[in] RequestMessage Request to send. + + @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess. + @retval Other Other errors. + +**/ +EFI_STATUS +HttpIoSendChunkedTransfer ( + IN HTTP_IO *HttpIo, + IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess, + IN EFI_HTTP_MESSAGE *RequestMessage +) +{ + EFI_STATUS Status; + EFI_HTTP_HEADER *NewHeaders; + EFI_HTTP_HEADER *ContentLengthHeader; + UINTN AddNewHeader; + UINTN HeaderCount; + CHAR8 *MessageBody; + UINTN MessageBodyLength; + UINTN ChunkLength; + CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN]; + EFI_HTTP_REQUEST_DATA *SentRequestData; + + AddNewHeader = 0; + NewHeaders = NULL; + MessageBody = NULL; + ContentLengthHeader = NULL; + MessageBodyLength = 0; + + switch (*SendChunkProcess) { + case HttpIoSendChunkHeaderZeroContent: + ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH); + if (ContentLengthHeader == NULL) { + AddNewHeader = 1; + } + + NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + AddNewHeader) * sizeof(EFI_HTTP_HEADER)); + CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER)); + if (AddNewHeader == 0) { + // + // Override content-length to Transfer-Encoding. + // + ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH); + ContentLengthHeader->FieldName = NULL; + ContentLengthHeader->FieldValue = NULL; + } else { + ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount; + } + HttpSetFieldNameAndValue (ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED); + HeaderCount = RequestMessage->HeaderCount + AddNewHeader; + MessageBodyLength = 0; + MessageBody = NULL; + SentRequestData = RequestMessage->Data.Request; + break; + + case HttpIoSendChunkContent: + HeaderCount = 0; + NewHeaders = NULL; + SentRequestData = NULL; + if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) { + MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD; + } else { + MessageBodyLength = RequestMessage->BodyLength; + } + AsciiSPrint ( + ChunkLengthStr, + HTTP_IO_CHUNK_SIZE_STRING_LEN, + "%x%c%c", + MessageBodyLength, + CHUNKED_TRANSFER_CODING_CR, + CHUNKED_TRANSFER_CODING_LF + ); + ChunkLength = AsciiStrLen (ChunkLengthStr); + MessageBody = AllocatePool (ChunkLength + MessageBodyLength + 2); + if (MessageBody == NULL) { + DEBUG((DEBUG_ERROR, "Not enough memory for chunk transfer\n")); + return EFI_OUT_OF_RESOURCES; + } + // + // Build up the chunk transfer paylaod. + // + CopyMem (MessageBody, ChunkLengthStr, ChunkLength); + CopyMem (MessageBody + ChunkLength, RequestMessage->Body, MessageBodyLength); + *(MessageBody + ChunkLength + MessageBodyLength) = CHUNKED_TRANSFER_CODING_CR; + *(MessageBody + ChunkLength + MessageBodyLength + 1) = CHUNKED_TRANSFER_CODING_LF; + // + // Change variables for the next chunk trasnfer. + // + RequestMessage->BodyLength -= MessageBodyLength; + RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength); + MessageBodyLength += (ChunkLength + 2); + if (RequestMessage->BodyLength == 0) { + *SendChunkProcess = HttpIoSendChunkEndChunk; + } + break; + + case HttpIoSendChunkEndChunk: + HeaderCount = 0; + NewHeaders = NULL; + SentRequestData = NULL; + AsciiSPrint ( + ChunkLengthStr, + HTTP_IO_CHUNK_SIZE_STRING_LEN, + "0%c%c%c%c", + CHUNKED_TRANSFER_CODING_CR, + CHUNKED_TRANSFER_CODING_LF, + CHUNKED_TRANSFER_CODING_CR, + CHUNKED_TRANSFER_CODING_LF + ); + MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr)); + if (MessageBody == NULL) { + DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n")); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr)); + MessageBodyLength = AsciiStrLen (ChunkLengthStr); + *SendChunkProcess = HttpIoSendChunkFinish; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + Status = HttpIoSendRequest ( + HttpIo, + SentRequestData, + HeaderCount, + NewHeaders, + MessageBodyLength, + MessageBody + ); + if (ContentLengthHeader != NULL) { + if (ContentLengthHeader->FieldName != NULL) { + FreePool (ContentLengthHeader->FieldName); + } + if (ContentLengthHeader->FieldValue != NULL) { + FreePool (ContentLengthHeader->FieldValue); + } + } + if (NewHeaders != NULL) { + FreePool (NewHeaders); + } + if (MessageBody != NULL) { + FreePool (MessageBody); + } + return Status; +} + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] HeaderCount Number of headers in Headers. + @param[in] Headers Array containing list of HTTP headers. + @param[out] ChunkListHead A pointer to receive list head + of chunked data. Caller has to + release memory of ChunkListHead + and all list entries. + @param[out] ContentLength Total content length + + @retval EFI_SUCCESS The HTTP chunked transfer is received. + @retval EFI_NOT_FOUND No chunked transfer coding header found. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_INVALID_PARAMETER Improper parameters. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoGetChunkedTransferContent ( + IN HTTP_IO *HttpIo, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + OUT LIST_ENTRY **ChunkListHead, + OUT UINTN *ContentLength + ) +{ + EFI_HTTP_HEADER *Header; + CHAR8 ChunkSizeAscii [256]; + EFI_STATUS Status; + UINTN Index; + HTTP_IO_RESPONSE_DATA ResponseData; + UINTN TotalLength; + UINTN MaxTotalLength; + LIST_ENTRY *HttpChunks; + HTTP_IO_CHUNKS *ThisChunk; + LIST_ENTRY *ThisListEntry; + + if (ChunkListHead == NULL || ContentLength == NULL) { + return EFI_INVALID_PARAMETER; + } + + *ContentLength = 0; + Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING); + if (Header == NULL) { + return EFI_NOT_FOUND; + } + + if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) { + return EFI_NOT_FOUND; + } + // + // Loop to get all chunks. + // + TotalLength = 0; + MaxTotalLength = PcdGet32 (PcdMaxHttpChunkTransfer); + HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY)); + if (HttpChunks == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ExitDeleteChunks; + } + InitializeListHead (HttpChunks); + DEBUG ((DEBUG_INFO, " Chunked transfer\n")); + while (TRUE) { + ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA)); + ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH; + ResponseData.Body = ChunkSizeAscii; + Status = HttpIoRecvResponse ( + HttpIo, + FALSE, + &ResponseData + ); + if (EFI_ERROR (Status)) { + goto ExitDeleteChunks; + } + // + // Decoding Chunked Transfer Coding. + // Only decode chunk-size and last chunk. + // + DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode)); + // + // Break if this is last chunk. + // + if (ChunkSizeAscii [0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK) { + // + // Check if this is a valid Last-Chunk. + // + if ((ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_CR) || + (ChunkSizeAscii [2] != CHUNKED_TRANSFER_CODING_LF) + ) { + DEBUG ((DEBUG_ERROR, " This is an invalid Last-chunk\n")); + Status = EFI_INVALID_PARAMETER; + goto ExitDeleteChunks; + } + + Status = EFI_SUCCESS; + DEBUG ((DEBUG_INFO, " Last-chunk\n")); + ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS)); + if (ThisChunk == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ExitDeleteChunks; + } + + InitializeListHead (&ThisChunk->NextChunk); + ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF. + ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length); + if (ThisChunk->Data == NULL) { + FreePool ((UINT8 *)ThisChunk); + Status = EFI_OUT_OF_RESOURCES; + goto ExitDeleteChunks; + } + CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length); + TotalLength += ThisChunk->Length; + InsertTailList (HttpChunks, &ThisChunk->NextChunk); + break; + } + + // + // Get the chunk length + // + Index = 0; + while ((ChunkSizeAscii [Index] != CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) && + (ChunkSizeAscii [Index] != (CHAR8)CHUNKED_TRANSFER_CODING_CR) && + (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH)) { + Index ++; + } + + if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) { + Status = EFI_NOT_FOUND; + goto ExitDeleteChunks; + } + ChunkSizeAscii[Index] = 0; + AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength); + DEBUG ((DEBUG_INFO, " Length of this chunk %d\n", *ContentLength)); + // + // Receive the data; + // + ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS)); + if (ThisChunk == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ExitDeleteChunks; + } + ResponseData.BodyLength = *ContentLength; + ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength); + if (ResponseData.Body == NULL) { + FreePool (ThisChunk); + Status = EFI_OUT_OF_RESOURCES; + goto ExitDeleteChunks; + } + InitializeListHead (&ThisChunk->NextChunk); + ThisChunk->Length = *ContentLength; + ThisChunk->Data = ResponseData.Body; + InsertTailList (HttpChunks, &ThisChunk->NextChunk); + Status = HttpIoRecvResponse ( + HttpIo, + FALSE, + &ResponseData + ); + if (EFI_ERROR (Status)) { + goto ExitDeleteChunks; + } + // + // Read CRLF + // + ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA)); + ResponseData.BodyLength = 2; + ResponseData.Body = ChunkSizeAscii; + Status = HttpIoRecvResponse ( + HttpIo, + FALSE, + &ResponseData + ); + if (EFI_ERROR (Status)) { + goto ExitDeleteChunks; + } + // + // Verify the end of chunk payload. + // + if ((ChunkSizeAscii [0] != CHUNKED_TRANSFER_CODING_CR) || + (ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_LF) + ) { + DEBUG ((DEBUG_ERROR, " This is an invalid End-of-chunk notation.\n")); + goto ExitDeleteChunks; + } + TotalLength += *ContentLength; + if (TotalLength > MaxTotalLength) { + DEBUG ((DEBUG_ERROR, " Total chunk transfer payload exceeds the size defined by PcdMaxHttpChunkTransfer.\n")); + goto ExitDeleteChunks; + } + } + + *ContentLength = TotalLength; + *ChunkListHead = HttpChunks; + DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n", TotalLength)); + return EFI_SUCCESS; + +ExitDeleteChunks: + if (HttpChunks != NULL) { + while (!IsListEmpty(HttpChunks)) { + ThisListEntry = GetFirstNode (HttpChunks); + RemoveEntryList (ThisListEntry); + ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry; + if (ThisChunk->Data != NULL) { + FreePool (ThisChunk->Data); + } + FreePool(ThisListEntry); + } + FreePool (HttpChunks); + } + return Status; +} -- cgit v1.2.3