alder_lake_bios/Insyde/InsydeNetworkPkg/Drivers/RestDxe/RestHttpClient.c

1188 lines
31 KiB
C

/** @file
REST protocol implementation.
;******************************************************************************
;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "RestDriver.h"
EFI_HTTP_HEADER mCommonHeader[] = {
{HTTP_HEADER_HOST, "localhost"},
{HTTP_HEADER_ACCEPT, "*/*"},
{HTTP_HEADER_USER_AGENT, "UefiRestProtocol/1.0"}
};
EFI_STATUS
RestHttpInput (
IN REST_HTTP_TOKEN *HttpToken,
IN BOOLEAN HeaderOnly
);
EFI_STATUS
RestHttpOutput (
IN REST_HTTP_TOKEN *HttpToken
);
/**
Check URL validation
TRUE : Valid
FALSE : Invalid
**/
BOOLEAN
IsURLValid (
UINT16 *URL
)
{
UINTN Index;
Index = 0;
while (URL[Index] != 0) {
/// Ascii charectors check, a-z, A-Z
if ((URL[Index] < 0x23) && (URL[Index] > 0x7E)) {
return FALSE;
}
switch (URL[Index]) {
case '<':
case '>':
case '\\':
case '^':
case '`':
case '{':
case '}':
return FALSE;
}
Index ++;
}
return TRUE;
}
//
// Update HOST name from HTTP request message.
//
EFI_STATUS
RestUpdateHttpHost (
OUT EFI_HTTP_MESSAGE *DestHttpMessage,
IN CHAR8 *HostName
)
{
EFI_STATUS Status;
UINTN Index;
Status = EFI_NOT_FOUND;
for (Index = 0; Index < DestHttpMessage->HeaderCount; Index++) {
if (AsciiStrCmp (DestHttpMessage->Headers[Index].FieldName, HTTP_HEADER_HOST) != 0) {
continue;
}
Status = EFI_SUCCESS;
DestHttpMessage->Headers[Index].FieldValue = HostName;
break;
}
return Status;
}
//
// This function will merge command HTTP headers and append http headers.
//
EFI_STATUS
RestMegerHttpHeaders (
IN EFI_HTTP_MESSAGE *AppendHeader,
OUT EFI_HTTP_MESSAGE *DestHttpMessage
)
{
UINTN AppendHeaderIndex;
UINTN AppendHeaderCounter;
UINTN CommonHeaderIndex;
UINTN CommonHeaderCounter;
UINTN RealHeaderIndex;
UINTN RealHeaderCounter;
EFI_HTTP_HEADER *AppendHttpHeaders;
EFI_HTTP_HEADER *HttpHeaders;
CHAR8 *FieldName;
//
// Calculate number of common header and copy common headers.
//
CommonHeaderCounter = sizeof (mCommonHeader) / sizeof (EFI_HTTP_HEADER);
AppendHeaderCounter = AppendHeader->HeaderCount;
AppendHttpHeaders = AppendHeader->Headers;
RealHeaderCounter = CommonHeaderCounter + AppendHeaderCounter;
HttpHeaders = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * RealHeaderCounter);
if (HttpHeaders == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Append headers into HttpHeaders
// RealHeaderIndex push to end of CommonHeaders, to append the header not exist in common headers.
//
RealHeaderIndex = CommonHeaderCounter;
CopyMem (HttpHeaders, mCommonHeader, CommonHeaderCounter * sizeof (EFI_HTTP_HEADER));
for (AppendHeaderIndex = 0; AppendHeaderIndex < AppendHeaderCounter; AppendHeaderIndex++) {
FieldName = AppendHttpHeaders[AppendHeaderIndex].FieldName;
for (CommonHeaderIndex = 0; CommonHeaderIndex < CommonHeaderCounter; CommonHeaderIndex++) {
if (AsciiStrCmp (HttpHeaders[CommonHeaderIndex].FieldName, FieldName) != 0) {
continue;
}
HttpHeaders[CommonHeaderIndex].FieldValue = AppendHttpHeaders[AppendHeaderIndex].FieldValue;
break;
}
if (CommonHeaderIndex < CommonHeaderCounter) {
//
// Value overrided by AppendHttpHeaders.
//
continue;
}
//
// Insert append header to RealHeaders.
//
HttpHeaders[RealHeaderIndex].FieldName = AppendHttpHeaders[AppendHeaderIndex].FieldName;
HttpHeaders[RealHeaderIndex].FieldValue = AppendHttpHeaders[AppendHeaderIndex].FieldValue;
RealHeaderIndex++;
}
DestHttpMessage->HeaderCount = RealHeaderIndex;
DestHttpMessage->Headers = HttpHeaders;
return EFI_SUCCESS;
}
//
// Callback to parsing received HTTP Package
//
EFI_STATUS
RestHttpParserCallback (
IN HTTP_BODY_PARSE_EVENT EventType,
IN CHAR8 *Data,
IN UINTN Length,
IN VOID *Context
)
{
//
// Chunk transfer
// This function must be review and debug.
//
REST_HTTP_TOKEN *HttpToken;
UINT8 *BufferPtr;
UINTN BufferOffser;
HttpToken = Context;
BufferOffser = HttpToken->ChunkDataLength;
if (EventType == BodyParseEventOnData) {
if (HttpToken->ChunkDataBuffer == NULL) {
BufferPtr = AllocateZeroPool (Length);
} else {
BufferPtr = ReallocatePool (HttpToken->ChunkDataLength, HttpToken->ChunkDataLength + Length, HttpToken->ChunkDataBuffer);
}
if (BufferPtr == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (BufferPtr + BufferOffser, Data, Length);
HttpToken->ChunkDataBuffer = BufferPtr;
HttpToken->ChunkDataLength += Length;
}
return EFI_SUCCESS;
}
//
// For DEBUG
//
EFI_STATUS
RestHttpStatusDump (
EFI_HTTP_MESSAGE *HttpMessage
)
{
UINTN Index;
if (!DEBUG_MODE) {
return EFI_NOT_READY;
}
AsciiPrint ("HTTP return Status : ", HttpMessage->Data.Response->StatusCode);
for (Index = 0; Index < HttpMessage->HeaderCount; Index++) {
AsciiPrint ("%a : ", HttpMessage->Headers[Index].FieldName);
AsciiPrint ("%a\n", HttpMessage->Headers[Index].FieldValue);
}
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpHeaderDump (
EFI_HTTP_MESSAGE *HttpMessage
)
{
UINTN Index;
if (!DEBUG_MODE) {
return EFI_NOT_READY;
}
AsciiPrint ("HTTP header\n");
for (Index = 0; Index < HttpMessage->HeaderCount; Index++) {
AsciiPrint ("%a : ", HttpMessage->Headers[Index].FieldName);
AsciiPrint ("%a\n", HttpMessage->Headers[Index].FieldValue);
}
return EFI_SUCCESS;
}
/**
Notify the callback function when an event is triggered.
@param[in] Event The triggered event.
@param[in] Context The opaque parameter to the function.
**/
VOID
RestHttpClientCommonNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
*((BOOLEAN *) Context) = TRUE;
}
EFI_STATUS
RestHttpGetDnsFromIp4Config2Data (
REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
UINTN DnsServerCnt;
EFI_IPv4_ADDRESS *DnsServers;
EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
Ip4Config2 = HttpToken->Ip4Config2;
DnsServers = NULL;
DnsServerCnt = 0;
Status = Ip4Config2->GetData (
Ip4Config2,
Ip4Config2DataTypeDnsServer,
&DnsServerCnt,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
DnsServers = AllocateZeroPool (DnsServerCnt);
if (DnsServers == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Ip4Config2->GetData (
Ip4Config2,
Ip4Config2DataTypeDnsServer,
&DnsServerCnt,
DnsServers
);
}
if (EFI_ERROR (Status)) {
FreePool (DnsServers);
return Status;
}
HttpToken->DnsServerCounter = DnsServerCnt / sizeof (EFI_IPv4_ADDRESS);
HttpToken->DnsServerList = DnsServers;
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpDns4Configure (
REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
EFI_DNS4_PROTOCOL *Dns4;
EFI_DNS4_CONFIG_DATA Dns4ConfigData;
ZeroMem (&Dns4ConfigData, sizeof (EFI_DNS4_CONFIG_DATA));
Dns4ConfigData.DnsServerListCount = HttpToken->DnsServerCounter;
Dns4ConfigData.DnsServerList = HttpToken->DnsServerList;
Dns4ConfigData.EnableDnsCache = TRUE;
/// this setting should be review.
Dns4ConfigData.UseDefaultSetting = TRUE;
Dns4ConfigData.Protocol = EFI_IP_PROTO_UDP;
Dns4 = HttpToken->Dns4;
if (Dns4 == NULL) {
AsciiPrint ("DNS protocol is not exist!\n");
return EFI_NOT_READY;
}
Status = Dns4->Configure (Dns4, &Dns4ConfigData);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpDns4TranslateAddress (
REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
EFI_DNS4_PROTOCOL *Dns4;
EFI_DNS4_COMPLETION_TOKEN Dns4Token;
BOOLEAN Dns4IsDone;
UINT16 *HostName;
UINTN HostNameLength;
HostNameLength = AsciiStrLen (HttpToken->HostName);
HostName = AllocateZeroPool (HostNameLength * sizeof (UINT16));
if (HostName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (&Dns4Token, sizeof (EFI_DNS4_COMPLETION_TOKEN));
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
RestHttpClientCommonNotify,
&Dns4IsDone,
&Dns4Token.Event
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Start asynchronous name resolution.
//
Dns4 = HttpToken->Dns4;
Dns4Token.Status = EFI_NOT_READY;
Dns4IsDone = FALSE;
AsciiStrToUnicodeStrS (HttpToken->HostName, HostName, HostNameLength * sizeof (UINT16));
Status = Dns4->HostNameToIp (Dns4, HostName, &Dns4Token);
if (EFI_ERROR (Status)) {
goto EXIT;
}
while (!Dns4IsDone) {
Dns4->Poll (Dns4);
}
//
// This section should be review for parsing DNS if it has IPv4 and IPv6 both.
// Name resolution is done, check result.
//
Status = Dns4Token.Status;
if (!EFI_ERROR (Status)) {
if (Dns4Token.RspData.H2AData == NULL) {
Status = EFI_DEVICE_ERROR;
goto EXIT;
}
if (Dns4Token.RspData.H2AData->IpCount == 0 || Dns4Token.RspData.H2AData->IpList == NULL) {
Status = EFI_DEVICE_ERROR;
goto EXIT;
}
//
// Just for a test with parsing URI.DomanName, it will not using IPv4 address yet.
//
//IP4_COPY_ADDRESS (IpAddress, Dns4Token.RspData.H2AData->IpList);
Status = EFI_SUCCESS;
}
EXIT:
if (EFI_ERROR (Status)) {
HttpToken->ReturnHttpStatusCode = HTTP_STATUS_503_SERVICE_UNAVAILABLE;
}
if (HostName != NULL) {
FreePool (HostName);
}
gBS->CloseEvent (Dns4Token.Event);
return Status;
}
///
/// This function will refer PCD to choice DNS capability in IPv4 or IPv6.
/// If there is no PCD setting in PCD database, it will be false to use IPv4.
///
VOID
RestResolveDnsPolicy (
OUT BOOLEAN *IsIPv6
)
{
VOID *IsIPv6Ptr;
UINT8 Policy;
/// Check PCD exist in PCD database.
*IsIPv6 = FALSE;
IsIPv6Ptr = PcdGetExPtr (&gInsydeTokenSpaceGuid, PcdH2ORestIPv6DnsPolicy);
if (IsIPv6Ptr == NULL) {
return;
}
Policy = PcdGetEx8 (&gInsydeTokenSpaceGuid, PcdH2ORestIPv6DnsPolicy);
if (Policy != 0) {
*IsIPv6 = TRUE;
}
return;
}
EFI_STATUS
RestCreateHttpClientToken (
IN REST_BINDING_INSTANCE *Instance,
OUT REST_HTTP_TOKEN **RestHttpToken
)
{
EFI_STATUS Status;
REST_HTTP_TOKEN *HttpToken;
HttpToken = (REST_HTTP_TOKEN *) AllocateZeroPool (sizeof (REST_HTTP_TOKEN));
if (HttpToken == NULL) {
return EFI_OUT_OF_RESOURCES;
}
HttpToken->HttpHandle = Instance->HttpHandle;
Status = gBS->HandleProtocol (
HttpToken->HttpHandle,
&gEfiHttpProtocolGuid,
(VOID **) &HttpToken->Http
);
if (EFI_ERROR(Status)) {
return Status;
}
Status = NetLibCreateServiceChild (
Instance->ControllerHandle,
gImageHandle,
&gEfiDns4ServiceBindingProtocolGuid,
&HttpToken->Dns4Handle
);
if (EFI_ERROR(Status)) {
HttpToken->Dns4 = NULL;
} else {
Status = gBS->HandleProtocol (
HttpToken->Dns4Handle,
&gEfiDns4ProtocolGuid,
(VOID **) &HttpToken->Dns4
);
if (EFI_ERROR(Status)) {
HttpToken->Dns4 = NULL;
}
}
Status = gBS->HandleProtocol (
Instance->ControllerHandle,
&gEfiIp4Config2ProtocolGuid,
(VOID **) &HttpToken->Ip4Config2
);
if (EFI_ERROR (Status)) {
HttpToken->Ip4Config2 = NULL;
}
HttpToken->IsIpv6 = FALSE;
Status = gBS->HandleProtocol (
Instance->ControllerHandle,
&gEfiIp6ConfigProtocolGuid,
(VOID **) &HttpToken->Ip6Config
);
if (EFI_ERROR (Status)) {
HttpToken->Ip6Config = NULL;
} else {
RestResolveDnsPolicy (&HttpToken->IsIpv6);
}
*RestHttpToken = HttpToken;
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpClientParsingURI (
IN OUT REST_HTTP_TOKEN *RestHttpToken
)
{
EFI_STATUS Status;
CHAR8 *URL;
UINTN URLLen;
VOID *UrlParser;
UrlParser = NULL;
if (!IsURLValid (RestHttpToken->HttpRequest->Url)) {
RestHttpToken->ReturnHttpStatusCode = HTTP_STATUS_400_BAD_REQUEST;
return EFI_INVALID_PARAMETER;
}
//
// Translate UNICODE to ASCII and parsing URL to check status of URL from HttpRequest
//
URLLen = StrLen (RestHttpToken->HttpRequest->Url) + 1;
URL = (CHAR8 *) AllocateZeroPool (URLLen);
if (URL == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = UnicodeStrToAsciiStrS (RestHttpToken->HttpRequest->Url, URL, URLLen);
if (EFI_ERROR (Status)) {
goto FINISH;
}
Status = HttpParseUrl (
URL,
(UINT32) URLLen,
FALSE,
(VOID **) &UrlParser
);
if (EFI_ERROR (Status)) {
RestHttpToken->ReturnHttpStatusCode = HTTP_STATUS_400_BAD_REQUEST;
Status = EFI_INVALID_PARAMETER;
goto FINISH;
}
//
// Get HostName.
// It might be those cases.
// IPv4 address 192.168.111.1
// IPv6 address [2048:1096::1]
// Domain name www.domain.com
//
RestHttpToken->HostName = NULL;
Status = HttpUrlGetHostName (URL, UrlParser, &RestHttpToken->HostName);
if (!EFI_ERROR (Status)) {
goto FINISH;
}
//
// Try to translate HostName to IPv4, if it success, means the HostName is IPv4 address type.
//
Status = HttpUrlGetIp4 (URL, UrlParser, &RestHttpToken->RemoteAddr);
if (!EFI_ERROR (Status)) {
goto FINISH;
}
//
// Try to translate HostName to IPv6, if it success, means the HostName is IPv6 address type.
//
Status = HttpUrlGetIp6 (URL, UrlParser, &RestHttpToken->RemoteAddr6);
if (!EFI_ERROR (Status)) {
RestHttpToken->IsIpv6 = TRUE;
goto FINISH;
}
//
// Domain name need to parsing by DNS.
// This function will not check status from DNS, so it RestDxe will choose IPv4 or IPv6 HTTP services.
//
//// DNS detect function disabled, enable this function after review and resolve problem of DNS.
//
// //
// // Get DNS addresses from Ip4Config2 protocol.
// //
// if ((RestHttpToken->Ip4Config2 == NULL) || (RestHttpToken->Dns4 == NULL)) {
// RestHttpToken->ReturnHttpStatusCode = HTTP_STATUS_503_SERVICE_UNAVAILABLE;
// Status = EFI_UNSUPPORTED;
// goto FINISH;
// }
//
//
// Status = RestHttpGetDnsFromIp4Config2Data (RestHttpToken);
// if (EFI_ERROR (Status)) {
// goto FINISH;
// }
//
// Status = RestHttpDns4Configure (RestHttpToken);
// if (EFI_ERROR (Status)) {
// goto FINISH;
// }
//
// Status = RestHttpDns4TranslateAddress (RestHttpToken);
// if (EFI_ERROR (Status)) {
// goto FINISH;
// }
FINISH:
if (UrlParser != NULL) {
HttpUrlFreeParser (UrlParser);
}
FreePool (URL);
return Status;
}
EFI_STATUS
RestDestroyHttpClientToken (
REST_BINDING_INSTANCE *Instance,
REST_HTTP_TOKEN *HttpToken
)
{
if (HttpToken == NULL) {
return EFI_SUCCESS;
}
gBS->CloseEvent (HttpToken->TimeoutEvent);
if (HttpToken->HttpHandle != NULL) {
HttpToken->Http->Configure (HttpToken->Http, NULL);
// NetLibDestroyServiceChild (
// NicHandle,
// gImageHandle,
// &gEfiHttpServiceBindingProtocolGuid,
// HttpToken->HttpHandle
// );
}
if (HttpToken->Dns4Handle != NULL) {
NetLibDestroyServiceChild (
Instance->ControllerHandle,
gImageHandle,
&gEfiDns4ServiceBindingProtocolGuid,
HttpToken->Dns4Handle
);
}
if (HttpToken->DnsServerList != NULL) {
FreePool (HttpToken->DnsServerList);
}
FreePool (HttpToken);
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpClientConfigIpv4 (
IN REST_HTTP_TOKEN *HttpToken,
IN OUT EFI_HTTP_CONFIG_DATA *HttpConfigData
)
{
EFI_HTTPv4_ACCESS_POINT *Http4AccessPoint;
if (HttpToken->Ip4Config2 == NULL) {
return EFI_NOT_READY;
}
Http4AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv4_ACCESS_POINT));
if (Http4AccessPoint == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Http4AccessPoint->UseDefaultAddress = TRUE;
HttpConfigData->AccessPoint.IPv4Node = Http4AccessPoint;
return EFI_SUCCESS;
}
EFI_STATUS
RestHttpClientConfigIpv6 (
IN REST_HTTP_TOKEN *HttpToken,
IN OUT EFI_HTTP_CONFIG_DATA *HttpConfigData
)
{
EFI_STATUS Status;
EFI_HTTPv6_ACCESS_POINT *Http6AccessPoint;
EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
UINTN DataSize;
EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
if (HttpToken->Ip6Config == NULL) {
return EFI_NOT_READY;
}
IfInfo = NULL;
Http6AccessPoint = NULL;
Ip6Config = HttpToken->Ip6Config;
DataSize = 0;
Status = Ip6Config->GetData (
Ip6Config,
Ip6ConfigDataTypeInterfaceInfo,
&DataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
IfInfo = AllocateZeroPool (DataSize);
if (IfInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Ip6Config->GetData (
Ip6Config,
Ip6ConfigDataTypeInterfaceInfo,
&DataSize,
IfInfo
);
}
if (EFI_ERROR (Status)) {
goto EXIT;
}
if (IfInfo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto EXIT;
}
Http6AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv6_ACCESS_POINT));
if (Http6AccessPoint == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto EXIT;
}
/// Get lastest address, expect that is not link-local address
CopyMem (&Http6AccessPoint->LocalAddress, &(IfInfo->AddressInfo[IfInfo->AddressInfoCount-1].Address), sizeof (EFI_IPv6_ADDRESS));
HttpConfigData->AccessPoint.IPv6Node = Http6AccessPoint;
EXIT:
if (IfInfo != NULL) {
FreePool (IfInfo);
}
return Status;
}
EFI_STATUS
RestHttpClientConfig (
IN REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
EFI_EVENT Event;
EFI_HTTP_CONFIG_DATA *HttpConfigData;
HttpConfigData = &HttpToken->HttpConfigData;
HttpConfigData->HttpVersion = HttpVersion11;
HttpConfigData->TimeOutMillisec = HTTP_RESPONSE_TIME_OUT;
HttpConfigData->LocalAddressIsIPv6 = HttpToken->IsIpv6;
if (!HttpToken->IsIpv6) {
Status = RestHttpClientConfigIpv4 (HttpToken, HttpConfigData);
} else {
Status = RestHttpClientConfigIpv6 (HttpToken, HttpConfigData);
}
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
Status = HttpToken->Http->Configure (HttpToken->Http, HttpConfigData);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Create events for variuos asynchronous operations.
//
Event = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
RestHttpClientCommonNotify,
&HttpToken->IsTxDone,
&Event
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
HttpToken->RequestEvent = Event;
Event = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
RestHttpClientCommonNotify,
&HttpToken->IsRxDone,
&Event
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
HttpToken->ResponseEvent = Event;
Event = NULL;
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
HttpToken->TimeoutEvent = Event;
return EFI_SUCCESS;
ON_ERROR:
if (HttpConfigData->AccessPoint.IPv4Node != NULL) {
FreePool (HttpConfigData->AccessPoint.IPv4Node);
}
if (HttpConfigData->AccessPoint.IPv6Node != NULL) {
FreePool (HttpConfigData->AccessPoint.IPv6Node);
}
return Status;
}
EFI_STATUS
RestHttpGetResponse (
IN REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
BOOLEAN HeaderOnly;
VOID *Parser;
UINT8 *ContentBuffer;
UINT8 *Block;
UINTN BlockSize;
UINTN ContentLength;
UINTN ReceivedLength;
EFI_HTTP_MESSAGE *Response;
ContentBuffer = NULL;
HeaderOnly = (HttpToken->HttpRequest->Method == HttpMethodHead) ? TRUE : FALSE;
Response = &HttpToken->Response;
//
// Transmit HTTP request
//
Status = RestHttpOutput (HttpToken);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Receive HTTP header
//
Status = RestHttpInput (HttpToken, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
Status = HttpInitMsgParser (
HttpToken->HttpRequest->Method,
HttpToken->ReturnHttpStatusCode,
Response->HeaderCount,
Response->Headers,
RestHttpParserCallback,
(VOID *) HttpToken,
&Parser
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// If "Content-Length" not in HTTP header, perhaps is identity transfer-coding.
//
Status = HttpGetEntityLength (Parser, &ContentLength);
if (!EFI_ERROR (Status)) {
HttpToken->IdentityMode = TRUE;
HttpToken->ContentLength = ContentLength;
}
if (HeaderOnly) {
goto EXIT;
}
//
// Start to receive HTTP body.
//
ReceivedLength = 0;
if (HttpToken->IdentityMode) {
ContentBuffer = AllocateZeroPool (ContentLength);
if (ContentBuffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto EXIT;
}
while (ReceivedLength < ContentLength) {
Response->Body = ContentBuffer + ReceivedLength;
if (ContentLength - ReceivedLength >= REST_BLOCK_SIZE) {
Response->BodyLength = REST_BLOCK_SIZE;
} else {
Response->BodyLength = ContentLength - ReceivedLength;
}
Status = RestHttpInput (HttpToken, FALSE);
if (EFI_ERROR (Status)) {
goto EXIT;
}
ReceivedLength += Response->BodyLength;
}
if (ReceivedLength > ContentLength) {
goto EXIT;
}
if (Response->Data.Response == NULL) {
Response->Data.Response = AllocateZeroPool (sizeof (EFI_HTTP_RESPONSE_DATA));
if (Response->Data.Response == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto EXIT;
}
}
Response->Data.Response->StatusCode = HttpToken->ReturnHttpStatusCode;
Response->Body = ContentBuffer;
Response->BodyLength = ReceivedLength;
} else {
//
// Chunk transfer
// This function must be review and debug.
//
Block = AllocateZeroPool (REST_BLOCK_SIZE);
BlockSize = REST_BLOCK_SIZE;
if (Block == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto EXIT;
}
HttpToken->ChunkDataBuffer = NULL;
HttpToken->ChunkDataLength = 0;
while (!HttpIsMessageComplete (Parser)) {
Response->Body = Block;
Response->BodyLength = BlockSize;
Status = RestHttpInput (HttpToken, FALSE);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Parse the new received block of the message-body, the block will be saved in cache.
//
Status = HttpParseMessageBody (
Parser,
Response->BodyLength,
Response->Body
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
}
//
// Get correct length from parser of Chunk transfer.
//
Status = HttpGetEntityLength (Parser, &ContentLength);
if (EFI_ERROR (Status)) {
goto EXIT;
}
Response->Data.Response->StatusCode = HttpToken->ReturnHttpStatusCode;
Response->Body = HttpToken->ChunkDataBuffer;
Response->BodyLength = HttpToken->ChunkDataLength;
}
EXIT:
if (EFI_ERROR (Status)) {
if (ContentBuffer != NULL) {
FreePool (ContentBuffer);
}
}
if (Parser != NULL) {
HttpFreeMsgParser (Parser);
}
if (!HeaderOnly && (HttpToken->IdentityMode)) {
if (ContentBuffer != NULL) {
Response->Body = ContentBuffer;
}
}
return Status;
}
EFI_STATUS
RestHttpOutput (
IN REST_HTTP_TOKEN *HttpToken
)
{
EFI_STATUS Status;
EFI_HTTP_MESSAGE RequestMessage;
EFI_HTTP_REQUEST_DATA RequestData;
EFI_HTTP_PROTOCOL *Http;
EFI_HTTP_TOKEN RequestToken;
EFI_EVENT TimeoutEvent;
ZeroMem (&RequestData, sizeof (EFI_HTTP_REQUEST_DATA));
ZeroMem (&RequestMessage, sizeof (EFI_HTTP_MESSAGE));
ZeroMem (&RequestToken, sizeof (EFI_HTTP_TOKEN));
Http = HttpToken->Http;
//
// Prepare HTTP Token
//
RequestData.Method = HttpToken->HttpRequest->Method;
RequestData.Url = HttpToken->HttpRequest->Url;
RequestMessage.Data.Request = &RequestData;
//
// Prepare HTTP header, if header coming from external
// Compare and merge it
//
if (HttpToken->Request.HeaderCount == 0) {
RequestMessage.HeaderCount = sizeof (mCommonHeader) / sizeof (EFI_HTTP_HEADER);
RequestMessage.Headers = mCommonHeader;
} else {
Status = RestMegerHttpHeaders (&HttpToken->Request, &RequestMessage);
if (EFI_ERROR (Status)) {
goto EXIT;
}
}
//
// Update HOST name in HTTP header.
//
Status = RestUpdateHttpHost (&RequestMessage, HttpToken->HostName);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Prepare HTTP body, if command is POST.
//
if ( HttpToken->HttpRequest->Method == HttpMethodPost ||
HttpToken->HttpRequest->Method == HttpMethodPatch ||
HttpToken->HttpRequest->Method == HttpMethodPut) {
if ((HttpToken->Request.BodyLength != 0) && (HttpToken->Request.Body != NULL)) {
RequestMessage.BodyLength = HttpToken->Request.BodyLength;
RequestMessage.Body = HttpToken->Request.Body;
}
}
RequestToken.Event = HttpToken->RequestEvent;
RequestToken.Message = &RequestMessage;
RequestToken.Status = EFI_NOT_READY;
HttpToken->IsTxDone = FALSE;
RestHttpHeaderDump (&HttpToken->Request);
//
// Start the timer, and wait Timeout seconds to receive the header packet.
//
TimeoutEvent = HttpToken->TimeoutEvent;
Status = gBS->SetTimer (TimeoutEvent, TimerRelative, HTTP_DELAY_TIME);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Transfer HTTP request to server.
//
Status = Http->Request (
Http,
&RequestToken
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Wait for finish.
//
while (!HttpToken->IsTxDone && EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
Http->Poll (Http);
}
gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
if (!HttpToken->IsTxDone) {
RequestToken.Status = EFI_TIMEOUT;
}
EXIT:
if (DEBUG_MODE) {
AsciiPrint ("%08x\n", RequestToken.Status);
}
if ((RequestMessage.Headers != NULL) && (RequestMessage.Headers != mCommonHeader)) {
FreePool (RequestMessage.Headers);
}
return RequestToken.Status;
}
EFI_STATUS
RestHttpInput (
IN REST_HTTP_TOKEN *HttpToken,
IN BOOLEAN HeaderOnly
)
{
EFI_STATUS Status;
EFI_HTTP_MESSAGE ResponseMessage;
EFI_HTTP_RESPONSE_DATA ResponseData;
EFI_HTTP_PROTOCOL *Http;
EFI_EVENT TimeoutEvent;
UINT8 *Block;
UINTN BlockSize;
EFI_HTTP_TOKEN ResponseToken;
Block = NULL;
BlockSize = 0;
if (!HeaderOnly) {
if ((HttpToken->Response.BodyLength != 0) && (HttpToken->Response.Body != NULL)) {
Block = HttpToken->Response.Body;
BlockSize = HttpToken->Response.BodyLength;
} else {
Block = AllocateZeroPool (REST_BLOCK_SIZE);
if (Block == NULL) {
return EFI_OUT_OF_RESOURCES;
}
BlockSize = REST_BLOCK_SIZE;
}
}
//
// Start the timer, and wait Timeout seconds to receive the header packet.
//
TimeoutEvent = HttpToken->TimeoutEvent;
Status = gBS->SetTimer (TimeoutEvent, TimerRelative, HTTP_DELAY_TIME);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Queue the response token to HTTP instances.
//
ZeroMem (&ResponseMessage, sizeof (EFI_HTTP_MESSAGE));
ZeroMem (&ResponseData, sizeof (EFI_HTTP_RESPONSE_DATA));
ZeroMem (&ResponseToken, sizeof (EFI_HTTP_TOKEN));
ResponseMessage.Data.Response = (HeaderOnly) ? &ResponseData : NULL;
ResponseMessage.Body = Block;
ResponseMessage.BodyLength = BlockSize;
ResponseMessage.HeaderCount = 0;
ResponseMessage.Headers = NULL;
ResponseToken.Message = &ResponseMessage;
ResponseToken.Status = EFI_NOT_READY;
ResponseToken.Event = HttpToken->ResponseEvent;
Http = HttpToken->Http;
HttpToken->IsRxDone = FALSE;
Status = Http->Response (
Http,
&ResponseToken
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Poll the network until receive finish.
//
while (!HttpToken->IsRxDone && EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
Http->Poll (Http);
}
gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
if (!HttpToken->IsRxDone) {
//
// Timeout occurs, cancel the response token.
//
Http->Cancel (Http, &ResponseToken);
Status = EFI_TIMEOUT;
goto EXIT;
}
if (DEBUG_MODE && HeaderOnly) {
AsciiPrint ("%08x %d %d\n", ResponseToken.Status, ResponseMessage.HeaderCount, ResponseMessage.BodyLength);
RestHttpHeaderDump (&ResponseMessage);
}
HttpToken->Response.Body = ResponseMessage.Body;
HttpToken->Response.BodyLength = ResponseMessage.BodyLength;
if ((ResponseMessage.HeaderCount != 0) && (ResponseMessage.Headers != NULL)) {
HttpToken->Response.HeaderCount = ResponseMessage.HeaderCount;
HttpToken->Response.Headers = ResponseMessage.Headers;
}
if (HeaderOnly) {
HttpToken->ReturnHttpStatusCode = ResponseMessage.Data.Response->StatusCode;
}
Status = ResponseToken.Status;
EXIT:
gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
return Status;
}