REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the NetworkPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
		
			
				
	
	
		
			886 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			886 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Http IO Helper Library.
 | |
| 
 | |
|   (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| **/
 | |
| 
 | |
| #include <Uefi.h>
 | |
| 
 | |
| #include <Protocol/Http.h>
 | |
| 
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/HttpIoLib.h>
 | |
| #include <Library/MemoryAllocationLib.h>
 | |
| #include <Library/PrintLib.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| 
 | |
| /**
 | |
|   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;
 | |
| }
 |