Newer
Older
AMI-Aptio-BIOS-Reversed / HttpUtilitiesDxe / HttpUtilitiesDxe.c
@Ajax Dong Ajax Dong 2 days ago 29 KB Init
/**
 * 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;
}