/**
* HttpUtilitiesDxe.c - AMI HTTP Utilities DXE driver
*
* Provides HTTP message building and parsing services for UEFI network
* stack drivers. Implements the HTTP_UTILITIES_PROTOCOL.
*
* Source: AmiNetworkPkg/UefiNetworkStack/Common/HttpUtilitiesDxe/
* HttpUtilitiesProtocol.c
*
* Build environment: VS2015, X64, DEBUG
* PDB: HttpUtilitiesDxe.pdb
*/
#include "HttpUtilitiesDxe.h"
/* -------------------------------------------------------------------
* Module Globals
* ------------------------------------------------------------------- */
EFI_GUID gEfiLoadedImageProtocolGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_GUID gHttpUtilitiesProtocolGuid = HTTP_UTILITIES_PROTOCOL_GUID;
EFI_GUID gAmiDebugLibraryProtocolGuid = AMI_DEBUG_LIBRARY_PROTOCOL_GUID;
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
VOID *gHobList = NULL;
/* AMI Debug Print Protocol interface (located at runtime) */
AMI_DEBUG_PRINT_PROTOCOL *gDebugPrintProtocol = NULL;
/* Protocol interface table for this driver */
HTTP_UTILITIES_PROTOCOL gHttpUtilitiesProtocol = {
0, /* Revision (set during entry) */
HttpUtilitiesBuild, /* Build */
HttpUtilitiesParse /* Parse */
};
/* The address of gHttpUtilitiesProtocol is passed as the interface
* when the protocol is installed:
* gBS->InstallMultipleProtocolInterfaces(
* &gImageHandle,
* &gHttpUtilitiesProtocolGuid,
* &gHttpUtilitiesProtocol,
* NULL);
*/
#define MAX_ASCII_STRING_LENGTH 1000000 /* 0xF4240 */
/* -------------------------------------------------------------------
* Memory Primitives (Internal, from BaseLib / BaseMemoryLib)
* ------------------------------------------------------------------- */
/**
* Internal byte copy supporting overlapping regions.
* If src < dst and the ranges overlap, copies backwards (from end to start);
* otherwise copies forwards in 8-byte chunks then tail bytes.
*/
CHAR8 *
EFIAPI
InternalCopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
CHAR8 *Dst8 = (CHAR8 *)Destination;
CONST CHAR8 *Src8 = (CONST CHAR8 *)Source;
if (Src8 < Dst8 && (Src8 + Length - 1) >= Dst8) {
/* Overlap: copy backwards */
CONST CHAR8 *SrcEnd = Src8 + Length - 1;
CHAR8 *DstEnd = Dst8 + Length - 1;
for (UINTN i = 0; i < Length; i++) {
*DstEnd-- = *SrcEnd--;
}
} else {
/* No overlap or dst < src: copy forwards */
UINTN Blocks = Length >> 3;
UINTN Tail = Length & 7;
/* Copy 8-byte blocks */
for (UINTN i = 0; i < Blocks; i++) {
*(UINT64 *)Dst8 = *(UINT64 *)Src8;
Dst8 += 8;
Src8 += 8;
}
/* Copy remaining bytes */
for (UINTN i = 0; i < Tail; i++) {
*Dst8++ = *Src8++;
}
}
return (CHAR8 *)Destination;
}
/**
* Internal zero-memory: write zeros in 8-byte blocks then tail bytes.
*/
VOID *
EFIAPI
InternalZeroMem (
OUT VOID *Buffer,
IN UINTN Length
)
{
UINT8 *Buf = (UINT8 *)Buffer;
UINTN Blocks = Length >> 3;
UINTN Tail = Length & 7;
for (UINTN i = 0; i < Blocks; i++) {
*(UINT64 *)Buf = 0;
Buf += 8;
}
for (UINTN i = 0; i < Tail; i++) {
*Buf++ = 0;
}
return Buffer;
}
/* -------------------------------------------------------------------
* String Primitives (from BaseLib)
* ------------------------------------------------------------------- */
/**
* Returns the length (in bytes, including null terminator) of an ASCII
* string. Asserts if the string exceeds MAX_ASCII_STRING_LENGTH.
*/
UINTN
EFIAPI
AsciiStrLen (
IN CONST CHAR8 *String
)
{
UINTN Length = 0;
ASSERT (String != NULL);
while (*String != '\0') {
Length++;
ASSERT (Length < MAX_ASCII_STRING_LENGTH);
String++;
}
return Length;
}
/**
* Case-insensitive ASCII string comparison.
* Converts both characters to uppercase before comparing.
* Returns 0 if strings are equal, non-zero otherwise.
*/
INTN
EFIAPI
AsciiStriCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString
)
{
CHAR8 F, S;
ASSERT (AsciiStrLen (FirstString) != (UINTN)-1);
ASSERT (AsciiStrLen (SecondString) != (UINTN)-1);
F = *FirstString;
if (F >= 'a' && F <= 'z') {
F -= ('a' - 'A');
}
S = *SecondString;
if (S >= 'a' && S <= 'z') {
S -= ('a' - 'A');
}
while (*FirstString != '\0') {
if (F != S) {
break;
}
FirstString++;
F = *FirstString;
if (F >= 'a' && F <= 'z') {
F -= ('a' - 'A');
}
S = FirstString[SecondString - FirstString];
if (S >= 'a' && S <= 'z') {
S -= ('a' - 'A');
}
}
return (INTN)F - (INTN)S;
}
/**
* Case-sensitive ASCII string comparison.
* Returns 0 if strings are equal, non-zero otherwise.
*/
INTN
EFIAPI
AsciiStrCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString
)
{
while (*FirstString != '\0' && *FirstString == *SecondString) {
FirstString++;
SecondString++;
}
return (INTN)(UINT8)*FirstString - (INTN)(UINT8)*SecondString;
}
/* -------------------------------------------------------------------
* Memory Copy / Zero Wrappers (from BaseMemoryLibRepStr)
* ------------------------------------------------------------------- */
/**
* CopyMem - Copies a memory buffer with bounds checking.
* Calls InternalCopyMem for the actual copy.
*/
VOID *
EFIAPI
CopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
if (Length == 0) {
return Destination;
}
ASSERT (Destination != NULL);
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)Destination));
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)Source));
if (Destination == Source) {
return Destination;
}
return InternalCopyMem (Destination, Source, Length);
}
/**
* ZeroMem - Zeroes a memory buffer with bounds checking.
* Calls InternalZeroMem for the actual zeroing.
*/
VOID *
EFIAPI
ZeroMem (
OUT VOID *Buffer,
IN UINTN Length
)
{
if (Length == 0) {
return Buffer;
}
ASSERT (Buffer != NULL);
ASSERT (Length <= (MAX_UINTN - (UINTN)Buffer + 1));
return InternalZeroMem (Buffer, Length);
}
/* -------------------------------------------------------------------
* Memory Allocation (from UefiMemoryAllocationLib)
* ------------------------------------------------------------------- */
/**
* Allocates a zero-filled buffer from boot services data pool.
*/
VOID *
EFIAPI
AllocateZeroPool (
IN UINTN Size
)
{
VOID *Buf;
Buf = AllocatePool (Size);
if (Buf != NULL) {
Buf = ZeroMem (Buf, Size);
}
return Buf;
}
/**
* AllocatePool wrapper - allocates from the EfiBootServicesData pool.
*/
VOID *
EFIAPI
AllocatePool (
IN UINTN Size
)
{
VOID *Buffer;
EFI_STATUS Status;
Status = gBS->AllocatePool (EfiBootServicesData, Size, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
return Buffer;
}
/**
* FreePool wrapper - frees a pool buffer.
*/
VOID
EFIAPI
FreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
if (Buffer == NULL) {
return;
}
Status = gBS->FreePool (Buffer);
ASSERT_EFI_ERROR (Status);
}
/* -------------------------------------------------------------------
* Utility functions (from BaseLib)
* ------------------------------------------------------------------- */
/**
* Reads an unaligned 64-bit integer from memory.
*/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(CONST UINT64 *)Buffer;
}
/* -------------------------------------------------------------------
* Debug / Assert (from UefiDriverEntryPoint + AMI Debug Protocol)
* ------------------------------------------------------------------- */
/**
* Locate the AMI Debug Print Protocol (gAmiDebugPrintProtocolGuid).
* Raises TPL to TPL_NOTIFY before locating to ensure thread safety.
* Only succeeds if current TPL <= TPL_CALLBACK.
*/
VOID *
EFIAPI
LocateDebugPrintProtocol (
VOID
)
{
EFI_STATUS Status;
UINTN PreviousTpl;
if (gDebugPrintProtocol != NULL) {
return gDebugPrintProtocol;
}
PreviousTpl = gBS->RaiseTPL (TPL_NOTIFY);
gBS->RestoreTPL (PreviousTpl);
if (PreviousTpl <= TPL_CALLBACK) {
Status = gBS->LocateProtocol (
&gAmiDebugLibraryProtocolGuid,
NULL,
(VOID **)&gDebugPrintProtocol
);
if (EFI_ERROR (Status)) {
gDebugPrintProtocol = NULL;
}
}
return gDebugPrintProtocol;
}
/**
* ASSERT implementation via AMI Debug Print Protocol.
* If the protocol is available, calls the Assert method.
*/
VOID
EFIAPI
Assert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *AssertText
)
{
AMI_DEBUG_PRINT_PROTOCOL *Protocol;
Protocol = (AMI_DEBUG_PRINT_PROTOCOL *)LocateDebugPrintProtocol ();
if (Protocol != NULL) {
Protocol->Assert (FileName, LineNumber, AssertText);
}
}
/**
* DebugPrint implementation via AMI Debug Print Protocol.
* If the protocol is available, calls the Print method.
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
AMI_DEBUG_PRINT_PROTOCOL *Protocol;
VA_LIST VaList;
UINTN Result;
Protocol = (AMI_DEBUG_PRINT_PROTOCOL *)LocateDebugPrintProtocol ();
if (Protocol != NULL) {
VA_START (VaList, Format);
Protocol->Print (ErrorLevel, Format, VaList);
VA_END (VaList);
}
return 0;
}
/* -------------------------------------------------------------------
* HOB List Initialization (from DxeHobLib)
* ------------------------------------------------------------------- */
/**
* Locate the EFI HOB list pointer from the system configuration table.
* Searches the ConfigurationTable for gEfiHobListGuid and caches
* the table pointer in gHobList.
*/
EFI_STATUS
EFIAPI
GetHobList (
VOID
)
{
UINTN Index;
gHobList = NULL;
if (gSystemTable->NumberOfTableEntries == 0) {
return EFI_NOT_FOUND;
}
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid (
&gEfiHobListGuid,
(EFI_GUID *)&gSystemTable->ConfigurationTable[Index].VendorGuid
))
{
gHobList = gSystemTable->ConfigurationTable[Index].Table;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
* Ensures the HOB list pointer is initialized.
* Called early in driver entry.
*/
VOID
EFIAPI
InitializeHobList (
VOID
)
{
EFI_STATUS Status;
if (gHobList == NULL) {
Status = GetHobList ();
ASSERT_EFI_ERROR (Status);
ASSERT (gHobList != NULL);
}
}
/* -------------------------------------------------------------------
* GUID Comparison (via ReadUnaligned64)
* ------------------------------------------------------------------- */
/**
* Compares two EFI_GUID values using 64-bit integer comparison.
* Returns TRUE if they are identical.
*/
BOOLEAN
EFIAPI
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return ReadUnaligned64 (&Guid1->Data1) == ReadUnaligned64 (&Guid2->Data1) &&
ReadUnaligned64 (&Guid1->Data4[0]) == ReadUnaligned64 (&Guid2->Data4[0]);
}
/* -------------------------------------------------------------------
* Header Pair Management
* ------------------------------------------------------------------- */
/**
* Frees an array of EFI_HTTP_HEADER pairs, releasing both the
* field name and value allocations.
*/
VOID
EFIAPI
FreeHeaderPairs (
IN EFI_HTTP_HEADER *Headers,
IN UINTN Count
)
{
UINTN Index;
if (Headers == NULL) {
return;
}
for (Index = 0; Index < Count; Index++) {
FreePool (Headers[Index].FieldName);
FreePool (Headers[Index].FieldValue);
}
FreePool (Headers);
}
/**
* Stores a header name-value pair into an EFI_HTTP_HEADER slot.
* Both FieldName and FieldValue are copied (allocated via AllocatePool).
*/
EFI_STATUS
EFIAPI
SetHeaderPair (
IN OUT EFI_HTTP_HEADER *Header,
IN CHAR8 *FieldName,
IN CHAR8 *FieldValue
)
{
UINTN NameSize = AsciiStrLen (FieldName) + 1;
UINTN ValueSize = AsciiStrLen (FieldValue) + 1;
Header->FieldName = AllocatePool (NameSize);
if (Header->FieldName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Header->FieldValue = AllocatePool (ValueSize);
if (Header->FieldValue == NULL) {
FreePool (Header->FieldName);
Header->FieldName = NULL;
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Header->FieldName, FieldName, NameSize);
CopyMem (Header->FieldValue, FieldValue, ValueSize);
(Header->FieldName)[NameSize - 1] = '\0';
(Header->FieldValue)[ValueSize - 1] = '\0';
return EFI_SUCCESS;
}
/**
* Finds an HTTP header entry by field name (case-insensitive).
* Returns a pointer to the matching header, or NULL if not found.
*/
EFI_HTTP_HEADER *
EFIAPI
FindHeaderByName (
IN EFI_HTTP_HEADER *Headers,
IN UINTN Count,
IN CHAR8 *FieldName
)
{
UINTN Index;
if (Count == 0 || Headers == NULL || FieldName == NULL) {
return NULL;
}
for (Index = 0; Index < Count; Index++) {
if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
return &Headers[Index];
}
}
return NULL;
}
/**
* Checks if a header field name matches any entry in a filter list
* (case-insensitive). Returns TRUE if a match is found.
*/
BOOLEAN
EFIAPI
MatchHeaderFilter (
IN CHAR8 **FilterList,
IN UINTN FilterCount,
IN CHAR8 *FieldName
)
{
UINTN Index;
for (Index = 0; Index < FilterCount; Index++) {
if (AsciiStrCmp (FilterList[Index], FieldName) == 0) {
return TRUE;
}
}
return FALSE;
}
/* -------------------------------------------------------------------
* HTTP Message Parsing Core
* ------------------------------------------------------------------- */
/**
* Parses the next header line from an HTTP message buffer.
*
* Scans for CRLF line endings, handling header folding (continuation
* lines starting with space or tab after CRLF).
*
* @param[in] Cursor Current position in the message buffer.
* @param[out] FieldName Pointer to the field name substring (modified
* in-place with null terminator).
* @param[out] FieldValue Pointer to the field value substring.
*
* @return Pointer past the parsed line, or NULL on end-of-headers.
*/
CHAR8 *
EFIAPI
ParseNextHeaderLine (
IN CHAR8 *Cursor,
OUT CHAR8 **FieldName,
OUT CHAR8 **FieldValue
)
{
CHAR8 *Colon;
CHAR8 *Value; /* start of value after colon */
CHAR8 *LineEnd;
CHAR8 *NextLine;
if (FieldName == NULL || FieldValue == NULL) {
return NULL;
}
*FieldName = NULL;
*FieldValue = NULL;
if (Cursor == NULL || *Cursor == '\0') {
return NULL;
}
/* Find the colon separating field name from value */
Colon = Cursor;
while (*Colon != '\0') {
if (*Colon == ':') {
break;
}
Colon++;
}
if (*Colon == '\0') {
return NULL; /* no colon found, not a valid header */
}
*Colon = '\0'; /* null-terminate the field name */
Value = Colon + 1;
/* Skip leading whitespace (spaces and tabs) after colon */
while (*Value == ' ' || *Value == '\t') {
Value++;
}
/* Handle line folding: CRLF followed by space/tab indicates continuation */
LineEnd = Value;
NextLine = Value;
while (*NextLine != '\0') {
/* Skip past the CRLF handling for folding continuation */
while (*NextLine != '\0') {
if (*NextLine == '\r') {
if (*(NextLine + 1) == '\n') {
/* Check for continuation: next line starts with space or tab */
if (*(NextLine + 2) == ' ' || *(NextLine + 2) == '\t') {
NextLine += 3; /* skip CR, LF, and the space/tab */
*(NextLine - 1) = ' '; /* replace with space */
LineEnd = NextLine;
break;
}
/* End of header line */
*NextLine = '\0';
*LineEnd = '\0';
*FieldName = Cursor;
*FieldValue = Value;
return NextLine + 2; /* skip past CRLF */
}
return NULL; /* CR without LF = invalid */
}
NextLine++;
LineEnd = NextLine;
}
}
/* End of buffer - return what we have */
*LineEnd = '\0';
*FieldName = Cursor;
*FieldValue = Value;
return NextLine;
}
/* -------------------------------------------------------------------
* HTTP_UTILITIES_PROTOCOL.Build
* ------------------------------------------------------------------- */
/**
* Builds an HTTP message from its constituent parts.
*
* Steps:
* 1. Filter input headers - only include those present in the
* filter list.
* 2. Concatenate filtered headers with the additional headers.
* 3. Compute total size: "FieldName: FieldValue\r\n" for each pair,
* then "\r\n" terminator, then body.
* 4. Allocate a contiguous buffer, format all parts into it.
*
* @param[in] This Pointer to the HTTP_UTILITIES_PROTOCOL
* @param[in] Method HTTP method (e.g. "GET", "POST") - OPTIONAL
* @param[in] Url Request URL - OPTIONAL
* @param[in] Headers Array of header name-value pairs - OPTIONAL
* @param[in] HeaderCount Number of header pairs
* @param[in] BodyLength Length of body data
* @param[in] Body Body data pointer - OPTIONAL
* @param[out] Message Allocated HTTP message buffer
* @param[out] MessageSize Size of the message buffer
*
* @return EFI_SUCCESS, EFI_INVALID_PARAMETER, EFI_OUT_OF_RESOURCES
*/
EFI_STATUS
EFIAPI
HttpUtilitiesBuild (
IN VOID *This,
IN CHAR8 *Method OPTIONAL,
IN CHAR8 *Url OPTIONAL,
IN EFI_HTTP_HEADER *Headers,
IN UINTN HeaderCount,
IN UINTN BodyLength,
IN VOID *Body OPTIONAL,
OUT CHAR8 **Message,
OUT UINTN *MessageSize
)
{
EFI_HTTP_HEADER *FilteredHeaders = NULL; /* Headers matching filter */
UINTN FilteredCount = 0;
EFI_HTTP_HEADER *AllHeaders = NULL; /* Filtered + Body headers */
UINTN TotalHeaders = 0;
EFI_HTTP_HEADER *Cursor;
CHAR8 *Buffer; /* output message buffer */
UINTN BufLen; /* current write position */
UINTN TotalSize;
UINTN Index;
EFI_STATUS Status;
/* The filter list identifies which headers to preserve.
* Defined as part of the protocol implementation; effectively
* filters "standard" HTTP headers from custom ones. */
static CHAR8 *mFilterList[] = {
"Content-Type",
"Content-Length",
"Connection",
"Transfer-Encoding",
"Cache-Control",
"Pragma",
"Date",
"Location",
"Server",
"User-Agent",
"Host",
"Accept",
"Accept-Encoding",
"Accept-Language",
"Content-Encoding",
"Content-Language",
"Content-Disposition",
"Content-Range",
"Content-MD5",
"Content-Type",
"Last-Modified",
"ETag",
"Expires",
"Set-Cookie"
};
UINTN mFilterCount = sizeof (mFilterList) / sizeof (mFilterList[0]);
*Message = NULL;
*MessageSize = 0;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
/* Step 1: Filter input headers */
if (Headers != NULL && HeaderCount > 0) {
/* Count matching headers */
for (Index = 0; Index < HeaderCount; Index++) {
if (MatchHeaderFilter (mFilterList, mFilterCount,
Headers[Index].FieldName)) {
FilteredCount++;
}
}
if (FilteredCount > 0) {
FilteredHeaders = AllocateZeroPool (FilteredCount * sizeof (EFI_HTTP_HEADER));
if (FilteredHeaders == NULL) {
return EFI_OUT_OF_RESOURCES;
}
/* Copy filtered headers */
Cursor = FilteredHeaders;
for (Index = 0; Index < HeaderCount; Index++) {
if (MatchHeaderFilter (mFilterList, mFilterCount,
Headers[Index].FieldName)) {
Status = SetHeaderPair (Cursor,
Headers[Index].FieldName,
Headers[Index].FieldValue);
if (EFI_ERROR (Status)) {
FreeHeaderPairs (FilteredHeaders, (UINTN)(Cursor - FilteredHeaders));
return Status;
}
Cursor++;
FilteredCount = (UINTN)(Cursor - FilteredHeaders);
}
}
}
}
/* Step 2: Allocate combined header array (filtered + body-length headers) */
TotalHeaders = FilteredCount + (Body != NULL ? 1 : 0);
AllHeaders = AllocateZeroPool (TotalHeaders * sizeof (EFI_HTTP_HEADER));
if (AllHeaders == NULL) {
FreeHeaderPairs (FilteredHeaders, FilteredCount);
return EFI_OUT_OF_RESOURCES;
}
/* Copy filtered headers into combined array */
Cursor = AllHeaders;
for (Index = 0; Index < FilteredCount; Index++) {
Status = SetHeaderPair (Cursor,
FilteredHeaders[Index].FieldName,
FilteredHeaders[Index].FieldValue);
if (EFI_ERROR (Status)) {
FreeHeaderPairs (FilteredHeaders, FilteredCount);
FreeHeaderPairs (AllHeaders, (UINTN)(Cursor - AllHeaders));
return Status;
}
Cursor++;
}
/* Add a Content-Length header if Body is present */
if (BodyLength > 0 && Body != NULL) {
CHAR8 LengthStr[32];
UINTN Len;
Len = AsciiStrLen ("Content-Length");
CopyMem (LengthStr, "Content-Length", Len + 1);
/* Body length may be 0 - we still include Content-Length: 0 */
Status = SetHeaderPair (Cursor, "Content-Length", "0");
if (!EFI_ERROR (Status)) {
Cursor++;
}
}
TotalHeaders = (UINTN)(Cursor - AllHeaders);
/* Step 3: Calculate total message size */
TotalSize = 0;
for (Index = 0; Index < TotalHeaders; Index++) {
/* "FieldName: FieldValue\r\n" */
TotalSize += AsciiStrLen (AllHeaders[Index].FieldName) + 2;
TotalSize += AsciiStrLen (AllHeaders[Index].FieldValue) + 2;
}
TotalSize += 2; /* final "\r\n" */
if (BodyLength > 0 && Body != NULL) {
TotalSize += BodyLength;
}
*MessageSize = TotalSize;
/* Step 4: Allocate output buffer */
Buffer = AllocatePool (TotalSize);
if (Buffer == NULL) {
FreeHeaderPairs (FilteredHeaders, FilteredCount);
FreeHeaderPairs (AllHeaders, TotalHeaders);
return EFI_OUT_OF_RESOURCES;
}
/* Step 5: Format the message */
BufLen = 0;
for (Index = 0; Index < TotalHeaders; Index++) {
UINTN NameLen = AsciiStrLen (AllHeaders[Index].FieldName);
UINTN ValueLen = AsciiStrLen (AllHeaders[Index].FieldValue);
CopyMem (Buffer + BufLen, AllHeaders[Index].FieldName, NameLen);
BufLen += NameLen;
CopyMem (Buffer + BufLen, ": ", 2);
BufLen += 2;
CopyMem (Buffer + BufLen, AllHeaders[Index].FieldValue, ValueLen);
BufLen += ValueLen;
CopyMem (Buffer + BufLen, "\r\n", 2);
BufLen += 2;
}
CopyMem (Buffer + BufLen, "\r\n", 2);
BufLen += 2;
if (BodyLength > 0 && Body != NULL) {
CopyMem (Buffer + BufLen, Body, BodyLength);
BufLen += BodyLength;
}
ASSERT (*MessageSize == BufLen);
*Message = Buffer;
/* Free temporary arrays */
FreeHeaderPairs (AllHeaders, TotalHeaders);
return EFI_SUCCESS;
}
/* -------------------------------------------------------------------
* HTTP_UTILITIES_PROTOCOL.Parse
* ------------------------------------------------------------------- */
/**
* Parses an HTTP message into its constituent header-value pairs.
*
* Scans the message buffer for header lines in the format:
* FieldName: FieldValue\r\n
* Handles:
* - Line folding (continuation lines)
* - Multiple headers with the same field name
* - Header names are case-insensitive during matching
* but preserved as-is in the output
* - The final "\r\n" marks end of headers; body follows
*
* @param[in] This Pointer to the HTTP_UTILITIES_PROTOCOL
* @param[in] Message Raw HTTP message buffer
* @param[in] MessageSize Size of the message buffer
* @param[out] Headers Array of parsed header pairs (caller frees)
* @param[out] HeaderCount Number of parsed headers
*
* @return EFI_SUCCESS, EFI_INVALID_PARAMETER, EFI_OUT_OF_RESOURCES,
* EFI_BUFFER_TOO_SMALL
*/
EFI_STATUS
EFIAPI
HttpUtilitiesParse (
IN VOID *This,
IN CHAR8 *Message,
IN UINTN MessageSize,
OUT EFI_HTTP_HEADER **Headers,
OUT UINTN *HeaderCount
)
{
EFI_STATUS Status;
CHAR8 *Cursor;
CHAR8 *FieldName;
CHAR8 *FieldValue;
UINTN Count;
EFI_HTTP_HEADER *ResultHeaders;
if (This == NULL || Message == NULL || Headers == NULL || HeaderCount == NULL) {
return EFI_INVALID_PARAMETER;
}
*Headers = NULL;
*HeaderCount = 0;
/* First pass: count headers to determine allocation size */
Cursor = Message;
Count = 0;
while (*Cursor != '\0') {
CHAR8 *Next;
FieldName = NULL;
FieldValue = NULL;
Next = ParseNextHeaderLine (Cursor, &FieldName, &FieldValue);
if (Next == NULL || FieldName == NULL || FieldValue == NULL) {
break;
}
Count++;
Cursor = Next;
}
if (Count == 0) {
*HeaderCount = 0;
return EFI_BUFFER_TOO_SMALL;
}
/* Second pass: allocate and extract headers */
ResultHeaders = AllocateZeroPool (Count * sizeof (EFI_HTTP_HEADER));
if (ResultHeaders == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Cursor = Message;
Count = 0;
while (*Cursor != '\0') {
CHAR8 NameBuf[256];
CHAR8 ValueBuf[4096];
UINTN i;
FieldName = NULL;
FieldValue = NULL;
Cursor = ParseNextHeaderLine (Cursor, &FieldName, &FieldValue);
if (Cursor == NULL || FieldName == NULL || FieldValue == NULL) {
break;
}
/* Copy the name to a temp buffer (preserving in-place null term) */
Status = SetHeaderPair (&ResultHeaders[Count], FieldName, FieldValue);
if (EFI_ERROR (Status)) {
FreeHeaderPairs (ResultHeaders, Count);
return Status;
}
Count++;
}
*Headers = ResultHeaders;
*HeaderCount = Count;
return EFI_SUCCESS;
}
/* -------------------------------------------------------------------
* Driver Entry / Unload
* ------------------------------------------------------------------- */
/**
* Driver unload handler.
*
* Disconnects all controllers using this driver's protocol, then
* uninstalls the HTTP_UTILITIES_PROTOCOL from the image handle.
*/
EFI_STATUS
EFIAPI
HttpUtilitiesDxeUnload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_HTTP_HEADER *Interface;
HandleCount = 0;
HandleBuffer = NULL;
/* Locate all handles consuming the HTTP Utilities Protocol */
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gHttpUtilitiesProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_FOUND) {
goto Uninstall;
}
return Status;
}
/* Close the protocol on each handle and uninstall */
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gHttpUtilitiesProtocolGuid,
(VOID **)&Interface,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
gBS->CloseProtocol (
HandleBuffer[Index],
&gHttpUtilitiesProtocolGuid,
ImageHandle,
NULL
);
}
}
FreePool (HandleBuffer);
Uninstall:
return gBS->UninstallMultipleProtocolInterfaces (
ImageHandle,
&gHttpUtilitiesProtocolGuid,
&gHttpUtilitiesProtocol,
NULL
);
}
/**
* Driver entry point.
*
* 1. Saves ImageHandle, SystemTable, BootServices, RuntimeServices globals.
* 2. Handles the loaded image protocol to set the unload handler.
* 3. Initializes the HOB list.
* 4. Installs the HTTP_UTILITIES_PROTOCOL.
*/
EFI_STATUS
EFIAPI
HttpUtilitiesDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
/* Save UEFI service pointers */
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
ASSERT (gImageHandle != NULL);
ASSERT (gSystemTable != NULL);
ASSERT (gBootServices != NULL);
ASSERT (gRuntimeServices != NULL);
/* Initialize the HOB list */
InitializeHobList ();
/* Open the loaded image protocol to register our unload handler */
Status = gBS->HandleProtocol (
ImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **)&LoadedImage
);
ASSERT_EFI_ERROR (Status);
if (!EFI_ERROR (Status)) {
LoadedImage->Unload = HttpUtilitiesDxeUnload;
}
/* Install the HTTP Utilities Protocol */
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gHttpUtilitiesProtocolGuid,
&gHttpUtilitiesProtocol,
NULL
);
return Status;
}