1. Do not use tab characters 2. No trailing white space in one line 3. All files must end with CRLF Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Liming Gao <liming.gao@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
		
			
				
	
	
		
			2081 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2081 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  This library is used to share code between UEFI network stack modules.
 | 
						|
  It provides the helper routines to parse the HTTP message byte stream.
 | 
						|
 | 
						|
Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
 | 
						|
This program and the accompanying materials
 | 
						|
are licensed and made available under the terms and conditions of the BSD License
 | 
						|
which accompanies this distribution.  The full text of the license may be found at<BR>
 | 
						|
http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "DxeHttpLib.h"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Decode a percent-encoded URI component to the ASCII character.
 | 
						|
 | 
						|
  Decode the input component in Buffer according to RFC 3986. The caller is responsible to make
 | 
						|
  sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))
 | 
						|
  in bytes.
 | 
						|
 | 
						|
  @param[in]    Buffer           The pointer to a percent-encoded URI component.
 | 
						|
  @param[in]    BufferLength     Length of Buffer in bytes.
 | 
						|
  @param[out]   ResultBuffer     Point to the buffer to store the decode result.
 | 
						|
  @param[out]   ResultLength     Length of decoded string in ResultBuffer in bytes.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            Successfully decoded the URI.
 | 
						|
  @retval EFI_INVALID_PARAMETER  Buffer is not a valid percent-encoded string.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
UriPercentDecode (
 | 
						|
  IN      CHAR8            *Buffer,
 | 
						|
  IN      UINT32            BufferLength,
 | 
						|
     OUT  CHAR8            *ResultBuffer,
 | 
						|
     OUT  UINT32           *ResultLength
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN           Index;
 | 
						|
  UINTN           Offset;
 | 
						|
  CHAR8           HexStr[3];
 | 
						|
 | 
						|
  if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Index = 0;
 | 
						|
  Offset = 0;
 | 
						|
  HexStr[2] = '\0';
 | 
						|
  while (Index < BufferLength) {
 | 
						|
    if (Buffer[Index] == '%') {
 | 
						|
      if (Index + 1 >= BufferLength || Index + 2 >= BufferLength ||
 | 
						|
          !NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {
 | 
						|
        return EFI_INVALID_PARAMETER;
 | 
						|
      }
 | 
						|
      HexStr[0] = Buffer[Index+1];
 | 
						|
      HexStr[1] = Buffer[Index+2];
 | 
						|
      ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);
 | 
						|
      Index += 3;
 | 
						|
    } else {
 | 
						|
      ResultBuffer[Offset] = Buffer[Index];
 | 
						|
      Index++;
 | 
						|
    }
 | 
						|
    Offset++;
 | 
						|
  }
 | 
						|
 | 
						|
  *ResultLength = (UINT32) Offset;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function return the updated state according to the input state and next character of
 | 
						|
  the authority.
 | 
						|
 | 
						|
  @param[in]       Char           Next character.
 | 
						|
  @param[in]       State          Current value of the parser state machine.
 | 
						|
  @param[in]       IsRightBracket TRUE if there is an sign ']' in the authority component and
 | 
						|
                                  indicates the next part is ':' before Port.
 | 
						|
 | 
						|
  @return          Updated state value.
 | 
						|
**/
 | 
						|
HTTP_URL_PARSE_STATE
 | 
						|
NetHttpParseAuthorityChar (
 | 
						|
  IN  CHAR8                  Char,
 | 
						|
  IN  HTTP_URL_PARSE_STATE   State,
 | 
						|
  IN  BOOLEAN                *IsRightBracket
 | 
						|
  )
 | 
						|
{
 | 
						|
 | 
						|
  //
 | 
						|
  // RFC 3986:
 | 
						|
  // The authority component is preceded by a double slash ("//") and is
 | 
						|
  // terminated by the next slash ("/"), question mark ("?"), or number
 | 
						|
  // sign ("#") character, or by the end of the URI.
 | 
						|
  //
 | 
						|
  if (Char == ' ' || Char == '\r' || Char == '\n') {
 | 
						|
    return UrlParserStateMax;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // authority   = [ userinfo "@" ] host [ ":" port ]
 | 
						|
  //
 | 
						|
  switch (State) {
 | 
						|
  case UrlParserUserInfo:
 | 
						|
    if (Char == '@') {
 | 
						|
      return UrlParserHostStart;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case UrlParserHost:
 | 
						|
  case UrlParserHostStart:
 | 
						|
    if (Char == '[') {
 | 
						|
      return UrlParserHostIpv6;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Char == ':') {
 | 
						|
      return UrlParserPortStart;
 | 
						|
    }
 | 
						|
 | 
						|
    return UrlParserHost;
 | 
						|
 | 
						|
  case UrlParserHostIpv6:
 | 
						|
    if (Char == ']') {
 | 
						|
      *IsRightBracket = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Char == ':' && *IsRightBracket) {
 | 
						|
      return UrlParserPortStart;
 | 
						|
    }
 | 
						|
    return UrlParserHostIpv6;
 | 
						|
 | 
						|
  case UrlParserPort:
 | 
						|
  case UrlParserPortStart:
 | 
						|
    return UrlParserPort;
 | 
						|
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  return State;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function parse the authority component of the input URL and update the parser.
 | 
						|
 | 
						|
  @param[in]       Url            The pointer to a HTTP URL string.
 | 
						|
  @param[in]       FoundAt        TRUE if there is an at sign ('@') in the authority component.
 | 
						|
  @param[in, out]  UrlParser      Pointer to the buffer of the parse result.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             Successfully parse the authority.
 | 
						|
  @retval EFI_INVALID_PARAMETER   The Url is invalid to parse the authority component.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
NetHttpParseAuthority (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      BOOLEAN            FoundAt,
 | 
						|
  IN OUT  HTTP_URL_PARSER    *UrlParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                 *Char;
 | 
						|
  CHAR8                 *Authority;
 | 
						|
  UINT32                Length;
 | 
						|
  HTTP_URL_PARSE_STATE  State;
 | 
						|
  UINT32                Field;
 | 
						|
  UINT32                OldField;
 | 
						|
  BOOLEAN               IsrightBracket;
 | 
						|
 | 
						|
  ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);
 | 
						|
 | 
						|
  //
 | 
						|
  // authority   = [ userinfo "@" ] host [ ":" port ]
 | 
						|
  //
 | 
						|
  if (FoundAt) {
 | 
						|
    State = UrlParserUserInfo;
 | 
						|
  } else {
 | 
						|
    State = UrlParserHost;
 | 
						|
  }
 | 
						|
 | 
						|
  IsrightBracket = FALSE;
 | 
						|
  Field = HTTP_URI_FIELD_MAX;
 | 
						|
  OldField = Field;
 | 
						|
  Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;
 | 
						|
  Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;
 | 
						|
  for (Char = Authority; Char < Authority + Length; Char++) {
 | 
						|
    State = NetHttpParseAuthorityChar (*Char, State, &IsrightBracket);
 | 
						|
    switch (State) {
 | 
						|
    case UrlParserStateMax:
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
 | 
						|
    case UrlParserHostStart:
 | 
						|
    case UrlParserPortStart:
 | 
						|
      continue;
 | 
						|
 | 
						|
    case UrlParserUserInfo:
 | 
						|
      Field = HTTP_URI_FIELD_USERINFO;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserHost:
 | 
						|
      Field = HTTP_URI_FIELD_HOST;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserHostIpv6:
 | 
						|
      Field = HTTP_URI_FIELD_HOST;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserPort:
 | 
						|
      Field = HTTP_URI_FIELD_PORT;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      ASSERT (FALSE);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Field not changed, count the length.
 | 
						|
    //
 | 
						|
    ASSERT (Field < HTTP_URI_FIELD_MAX);
 | 
						|
    if (Field == OldField) {
 | 
						|
      UrlParser->FieldData[Field].Length++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // New field start
 | 
						|
    //
 | 
						|
    UrlParser->FieldBitMap |= BIT (Field);
 | 
						|
    UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);
 | 
						|
    UrlParser->FieldData[Field].Length = 1;
 | 
						|
    OldField = Field;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function return the updated state according to the input state and next character of a URL.
 | 
						|
 | 
						|
  @param[in]       Char           Next character.
 | 
						|
  @param[in]       State          Current value of the parser state machine.
 | 
						|
 | 
						|
  @return          Updated state value.
 | 
						|
 | 
						|
**/
 | 
						|
HTTP_URL_PARSE_STATE
 | 
						|
NetHttpParseUrlChar (
 | 
						|
  IN  CHAR8                  Char,
 | 
						|
  IN  HTTP_URL_PARSE_STATE   State
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (Char == ' ' || Char == '\r' || Char == '\n') {
 | 
						|
    return UrlParserStateMax;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
 | 
						|
  //
 | 
						|
  // Request-URI    = "*" | absolute-URI | path-absolute | authority
 | 
						|
  //
 | 
						|
  // absolute-URI  = scheme ":" hier-part [ "?" query ]
 | 
						|
  // path-absolute = "/" [ segment-nz *( "/" segment ) ]
 | 
						|
  // authority   = [ userinfo "@" ] host [ ":" port ]
 | 
						|
  //
 | 
						|
  switch (State) {
 | 
						|
  case UrlParserUrlStart:
 | 
						|
    if (Char == '*' || Char == '/') {
 | 
						|
      return UrlParserPath;
 | 
						|
    }
 | 
						|
    return UrlParserScheme;
 | 
						|
 | 
						|
  case UrlParserScheme:
 | 
						|
    if (Char == ':') {
 | 
						|
      return UrlParserSchemeColon;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case UrlParserSchemeColon:
 | 
						|
    if (Char == '/') {
 | 
						|
      return UrlParserSchemeColonSlash;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case UrlParserSchemeColonSlash:
 | 
						|
    if (Char == '/') {
 | 
						|
      return UrlParserSchemeColonSlashSlash;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case UrlParserAtInAuthority:
 | 
						|
    if (Char == '@') {
 | 
						|
      return UrlParserStateMax;
 | 
						|
    }
 | 
						|
 | 
						|
  case UrlParserAuthority:
 | 
						|
  case UrlParserSchemeColonSlashSlash:
 | 
						|
    if (Char == '@') {
 | 
						|
      return UrlParserAtInAuthority;
 | 
						|
    }
 | 
						|
    if (Char == '/') {
 | 
						|
      return UrlParserPath;
 | 
						|
    }
 | 
						|
    if (Char == '?') {
 | 
						|
      return UrlParserQueryStart;
 | 
						|
    }
 | 
						|
    if (Char == '#') {
 | 
						|
      return UrlParserFragmentStart;
 | 
						|
    }
 | 
						|
    return UrlParserAuthority;
 | 
						|
 | 
						|
  case UrlParserPath:
 | 
						|
    if (Char == '?') {
 | 
						|
      return UrlParserQueryStart;
 | 
						|
    }
 | 
						|
    if (Char == '#') {
 | 
						|
      return UrlParserFragmentStart;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case UrlParserQuery:
 | 
						|
  case UrlParserQueryStart:
 | 
						|
    if (Char == '#') {
 | 
						|
      return UrlParserFragmentStart;
 | 
						|
    }
 | 
						|
    return UrlParserQuery;
 | 
						|
 | 
						|
  case UrlParserFragmentStart:
 | 
						|
    return UrlParserFragment;
 | 
						|
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  return State;
 | 
						|
}
 | 
						|
/**
 | 
						|
  Create a URL parser for the input URL string.
 | 
						|
 | 
						|
  This function will parse and dereference the input HTTP URL into it components. The original
 | 
						|
  content of the URL won't be modified and the result will be returned in UrlParser, which can
 | 
						|
  be used in other functions like NetHttpUrlGetHostName().
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    Length             Length of Url in bytes.
 | 
						|
  @param[in]    IsConnectMethod    Whether the Url is used in HTTP CONNECT method or not.
 | 
						|
  @param[out]   UrlParser          Pointer to the returned buffer to store the parse result.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully dereferenced the HTTP URL.
 | 
						|
  @retval EFI_INVALID_PARAMETER    UrlParser is NULL or Url is not a valid HTTP URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpParseUrl (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      UINT32             Length,
 | 
						|
  IN      BOOLEAN            IsConnectMethod,
 | 
						|
     OUT  VOID               **UrlParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  HTTP_URL_PARSE_STATE  State;
 | 
						|
  CHAR8                 *Char;
 | 
						|
  UINT32                Field;
 | 
						|
  UINT32                OldField;
 | 
						|
  BOOLEAN               FoundAt;
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  HTTP_URL_PARSER       *Parser;
 | 
						|
 | 
						|
  Parser = NULL;
 | 
						|
 | 
						|
  if (Url == NULL || Length == 0 || UrlParser == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));
 | 
						|
  if (Parser == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  if (IsConnectMethod) {
 | 
						|
    //
 | 
						|
    // According to RFC 2616, the authority form is only used by the CONNECT method.
 | 
						|
    //
 | 
						|
    State = UrlParserAuthority;
 | 
						|
  } else {
 | 
						|
    State = UrlParserUrlStart;
 | 
						|
  }
 | 
						|
 | 
						|
  Field = HTTP_URI_FIELD_MAX;
 | 
						|
  OldField = Field;
 | 
						|
  FoundAt = FALSE;
 | 
						|
  for (Char = Url; Char < Url + Length; Char++) {
 | 
						|
    //
 | 
						|
    // Update state machine according to next char.
 | 
						|
    //
 | 
						|
    State = NetHttpParseUrlChar (*Char, State);
 | 
						|
 | 
						|
    switch (State) {
 | 
						|
    case UrlParserStateMax:
 | 
						|
      FreePool (Parser);
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
 | 
						|
    case UrlParserSchemeColon:
 | 
						|
    case UrlParserSchemeColonSlash:
 | 
						|
    case UrlParserSchemeColonSlashSlash:
 | 
						|
    case UrlParserQueryStart:
 | 
						|
    case UrlParserFragmentStart:
 | 
						|
      //
 | 
						|
      // Skip all the delimiting char: "://" "?" "@"
 | 
						|
      //
 | 
						|
      continue;
 | 
						|
 | 
						|
    case UrlParserScheme:
 | 
						|
      Field = HTTP_URI_FIELD_SCHEME;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserAtInAuthority:
 | 
						|
      FoundAt = TRUE;
 | 
						|
    case UrlParserAuthority:
 | 
						|
      Field = HTTP_URI_FIELD_AUTHORITY;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserPath:
 | 
						|
      Field = HTTP_URI_FIELD_PATH;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserQuery:
 | 
						|
      Field = HTTP_URI_FIELD_QUERY;
 | 
						|
      break;
 | 
						|
 | 
						|
    case UrlParserFragment:
 | 
						|
      Field = HTTP_URI_FIELD_FRAGMENT;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      ASSERT (FALSE);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Field not changed, count the length.
 | 
						|
    //
 | 
						|
    ASSERT (Field < HTTP_URI_FIELD_MAX);
 | 
						|
    if (Field == OldField) {
 | 
						|
      Parser->FieldData[Field].Length++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // New field start
 | 
						|
    //
 | 
						|
    Parser->FieldBitMap |= BIT (Field);
 | 
						|
    Parser->FieldData[Field].Offset = (UINT32) (Char - Url);
 | 
						|
    Parser->FieldData[Field].Length = 1;
 | 
						|
    OldField = Field;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If has authority component, continue to parse the username, host and port.
 | 
						|
  //
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {
 | 
						|
    Status = NetHttpParseAuthority (Url, FoundAt, Parser);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      FreePool (Parser);
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  *UrlParser = Parser;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the Hostname from a HTTP URL.
 | 
						|
 | 
						|
  This function will return the HostName according to the Url and previous parse result ,and
 | 
						|
  it is the caller's responsibility to free the buffer returned in *HostName.
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
 | 
						|
  @param[out]   HostName           Pointer to a buffer to store the HostName.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully get the required component.
 | 
						|
  @retval EFI_INVALID_PARAMETER    Uri is NULL or HostName is NULL or UrlParser is invalid.
 | 
						|
  @retval EFI_NOT_FOUND            No hostName component in the URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpUrlGetHostName (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      VOID               *UrlParser,
 | 
						|
     OUT  CHAR8              **HostName
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                *Name;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINT32               ResultLength;
 | 
						|
  HTTP_URL_PARSER      *Parser;
 | 
						|
 | 
						|
  if (Url == NULL || UrlParser == NULL || HostName == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_URL_PARSER *) UrlParser;
 | 
						|
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
 | 
						|
  if (Name == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = UriPercentDecode (
 | 
						|
             Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
 | 
						|
             Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
 | 
						|
             Name,
 | 
						|
             &ResultLength
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    FreePool (Name);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  Name[ResultLength] = '\0';
 | 
						|
  *HostName = Name;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get the IPv4 address from a HTTP URL.
 | 
						|
 | 
						|
  This function will return the IPv4 address according to the Url and previous parse result.
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
 | 
						|
  @param[out]   Ip4Address         Pointer to a buffer to store the IP address.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully get the required component.
 | 
						|
  @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
 | 
						|
  @retval EFI_NOT_FOUND            No IPv4 address component in the URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpUrlGetIp4 (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      VOID               *UrlParser,
 | 
						|
     OUT  EFI_IPv4_ADDRESS   *Ip4Address
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                *Ip4String;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINT32               ResultLength;
 | 
						|
  HTTP_URL_PARSER      *Parser;
 | 
						|
 | 
						|
  if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_URL_PARSER *) UrlParser;
 | 
						|
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
 | 
						|
  if (Ip4String == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = UriPercentDecode (
 | 
						|
             Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
 | 
						|
             Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
 | 
						|
             Ip4String,
 | 
						|
             &ResultLength
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    FreePool (Ip4String);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  Ip4String[ResultLength] = '\0';
 | 
						|
  Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);
 | 
						|
  FreePool (Ip4String);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the IPv6 address from a HTTP URL.
 | 
						|
 | 
						|
  This function will return the IPv6 address according to the Url and previous parse result.
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
 | 
						|
  @param[out]   Ip6Address         Pointer to a buffer to store the IP address.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully get the required component.
 | 
						|
  @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
 | 
						|
  @retval EFI_NOT_FOUND            No IPv6 address component in the URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpUrlGetIp6 (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      VOID               *UrlParser,
 | 
						|
     OUT  EFI_IPv6_ADDRESS   *Ip6Address
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                *Ip6String;
 | 
						|
  CHAR8                *Ptr;
 | 
						|
  UINT32               Length;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINT32               ResultLength;
 | 
						|
  HTTP_URL_PARSER      *Parser;
 | 
						|
 | 
						|
  if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_URL_PARSER *) UrlParser;
 | 
						|
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // IP-literal = "[" ( IPv6address / IPvFuture  ) "]"
 | 
						|
  //
 | 
						|
  Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;
 | 
						|
  if (Length < 2) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Ptr    = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;
 | 
						|
  if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Ip6String = AllocatePool (Length);
 | 
						|
  if (Ip6String == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = UriPercentDecode (
 | 
						|
             Ptr + 1,
 | 
						|
             Length - 2,
 | 
						|
             Ip6String,
 | 
						|
             &ResultLength
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    FreePool (Ip6String);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  Ip6String[ResultLength] = '\0';
 | 
						|
  Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);
 | 
						|
  FreePool (Ip6String);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the port number from a HTTP URL.
 | 
						|
 | 
						|
  This function will return the port number according to the Url and previous parse result.
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
 | 
						|
  @param[out]   Port               Pointer to a buffer to store the port number.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully get the required component.
 | 
						|
  @retval EFI_INVALID_PARAMETER    Uri is NULL or Port is NULL or UrlParser is invalid.
 | 
						|
  @retval EFI_NOT_FOUND            No port number in the URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpUrlGetPort (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      VOID               *UrlParser,
 | 
						|
     OUT  UINT16             *Port
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                *PortString;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINTN                Index;
 | 
						|
  UINTN                Data;
 | 
						|
  UINT32               ResultLength;
 | 
						|
  HTTP_URL_PARSER      *Parser;
 | 
						|
 | 
						|
  if (Url == NULL || UrlParser == NULL || Port == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  *Port = 0;
 | 
						|
  Index = 0;
 | 
						|
 | 
						|
  Parser = (HTTP_URL_PARSER *) UrlParser;
 | 
						|
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);
 | 
						|
  if (PortString == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = UriPercentDecode (
 | 
						|
             Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,
 | 
						|
             Parser->FieldData[HTTP_URI_FIELD_PORT].Length,
 | 
						|
             PortString,
 | 
						|
             &ResultLength
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  PortString[ResultLength] = '\0';
 | 
						|
 | 
						|
  while (Index < ResultLength) {
 | 
						|
    if (!NET_IS_DIGIT (PortString[Index])) {
 | 
						|
      Status = EFI_INVALID_PARAMETER;
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
    Index ++;
 | 
						|
  }
 | 
						|
 | 
						|
  Status =  AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **) NULL, &Data);
 | 
						|
 | 
						|
  if (Data > HTTP_URI_PORT_MAX_NUM) {
 | 
						|
    Status = EFI_INVALID_PARAMETER;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  *Port = (UINT16) Data;
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
  FreePool (PortString);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the Path from a HTTP URL.
 | 
						|
 | 
						|
  This function will return the Path according to the Url and previous parse result,and
 | 
						|
  it is the caller's responsibility to free the buffer returned in *Path.
 | 
						|
 | 
						|
  @param[in]    Url                The pointer to a HTTP URL string.
 | 
						|
  @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
 | 
						|
  @param[out]   Path               Pointer to a buffer to store the Path.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully get the required component.
 | 
						|
  @retval EFI_INVALID_PARAMETER    Uri is NULL or HostName is NULL or UrlParser is invalid.
 | 
						|
  @retval EFI_NOT_FOUND            No hostName component in the URL.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpUrlGetPath (
 | 
						|
  IN      CHAR8              *Url,
 | 
						|
  IN      VOID               *UrlParser,
 | 
						|
     OUT  CHAR8              **Path
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                *PathStr;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINT32               ResultLength;
 | 
						|
  HTTP_URL_PARSER      *Parser;
 | 
						|
 | 
						|
  if (Url == NULL || UrlParser == NULL || Path == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_URL_PARSER *) UrlParser;
 | 
						|
 | 
						|
  if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);
 | 
						|
  if (PathStr == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = UriPercentDecode (
 | 
						|
             Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,
 | 
						|
             Parser->FieldData[HTTP_URI_FIELD_PATH].Length,
 | 
						|
             PathStr,
 | 
						|
             &ResultLength
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    FreePool (PathStr);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  PathStr[ResultLength] = '\0';
 | 
						|
  *Path = PathStr;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Release the resource of the URL parser.
 | 
						|
 | 
						|
  @param[in]    UrlParser            Pointer to the parser.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
HttpUrlFreeParser (
 | 
						|
  IN      VOID               *UrlParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  FreePool (UrlParser);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find a specified header field according to the field name.
 | 
						|
 | 
						|
  @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
 | 
						|
  @param[in]   Headers          Array containing list of HTTP headers.
 | 
						|
  @param[in]   FieldName        Null terminated string which describes a field name.
 | 
						|
 | 
						|
  @return    Pointer to the found header or NULL.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_HTTP_HEADER *
 | 
						|
EFIAPI
 | 
						|
HttpFindHeader (
 | 
						|
  IN  UINTN                HeaderCount,
 | 
						|
  IN  EFI_HTTP_HEADER      *Headers,
 | 
						|
  IN  CHAR8                *FieldName
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                 Index;
 | 
						|
 | 
						|
  if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < HeaderCount; Index++){
 | 
						|
    //
 | 
						|
    // Field names are case-insensitive (RFC 2616).
 | 
						|
    //
 | 
						|
    if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
 | 
						|
      return &Headers[Index];
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
typedef enum {
 | 
						|
  BodyParserBodyStart,
 | 
						|
  BodyParserBodyIdentity,
 | 
						|
  BodyParserChunkSizeStart,
 | 
						|
  BodyParserChunkSize,
 | 
						|
  BodyParserChunkSizeEndCR,
 | 
						|
  BodyParserChunkExtStart,
 | 
						|
  BodyParserChunkDataStart,
 | 
						|
  BodyParserChunkDataEnd,
 | 
						|
  BodyParserChunkDataEndCR,
 | 
						|
  BodyParserTrailer,
 | 
						|
  BodyParserLastCRLF,
 | 
						|
  BodyParserLastCRLFEnd,
 | 
						|
  BodyParserComplete,
 | 
						|
  BodyParserStateMax
 | 
						|
} HTTP_BODY_PARSE_STATE;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  BOOLEAN                       IgnoreBody;    // "MUST NOT" include a message-body
 | 
						|
  BOOLEAN                       IsChunked;     // "chunked" transfer-coding.
 | 
						|
  BOOLEAN                       ContentLengthIsValid;
 | 
						|
  UINTN                         ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
 | 
						|
 | 
						|
  HTTP_BODY_PARSER_CALLBACK     Callback;
 | 
						|
  VOID                          *Context;
 | 
						|
  UINTN                         ParsedBodyLength;
 | 
						|
  HTTP_BODY_PARSE_STATE         State;
 | 
						|
  UINTN                         CurrentChunkSize;
 | 
						|
  UINTN                         CurrentChunkParsedSize;
 | 
						|
} HTTP_BODY_PARSER;
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Convert an Ascii char to its uppercase.
 | 
						|
 | 
						|
  @param[in]       Char           Ascii character.
 | 
						|
 | 
						|
  @return          Uppercase value of the input Char.
 | 
						|
 | 
						|
**/
 | 
						|
CHAR8
 | 
						|
HttpIoCharToUpper (
 | 
						|
  IN      CHAR8                    Char
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (Char >= 'a' && Char <= 'z') {
 | 
						|
    return  Char - ('a' - 'A');
 | 
						|
  }
 | 
						|
 | 
						|
  return Char;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Convert an hexadecimal char to a value of type UINTN.
 | 
						|
 | 
						|
  @param[in]       Char           Ascii character.
 | 
						|
 | 
						|
  @return          Value translated from Char.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
HttpIoHexCharToUintn (
 | 
						|
  IN CHAR8           Char
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (Char >= '0' && Char <= '9') {
 | 
						|
    return Char - '0';
 | 
						|
  }
 | 
						|
 | 
						|
  return (10 + HttpIoCharToUpper (Char) - 'A');
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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
 | 
						|
HttpIoParseContentLengthHeader (
 | 
						|
  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);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Check whether the HTTP message is using the "chunked" transfer-coding.
 | 
						|
 | 
						|
  @param[in]    HeaderCount        Number of HTTP header structures in Headers.
 | 
						|
  @param[in]    Headers            Array containing list of HTTP headers.
 | 
						|
 | 
						|
  @return       The message is "chunked" transfer-coding (TRUE) or not (FALSE).
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
HttpIoIsChunked (
 | 
						|
  IN   UINTN                    HeaderCount,
 | 
						|
  IN   EFI_HTTP_HEADER          *Headers
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_HTTP_HEADER       *Header;
 | 
						|
 | 
						|
 | 
						|
  Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
 | 
						|
  if (Header == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether the HTTP message should have a message-body.
 | 
						|
 | 
						|
  @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.
 | 
						|
  @param[in]    StatusCode         Response status code returned by the remote host.
 | 
						|
 | 
						|
  @return       The message should have a message-body (FALSE) or not (TRUE).
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
HttpIoNoMessageBody (
 | 
						|
  IN   EFI_HTTP_METHOD          Method,
 | 
						|
  IN   EFI_HTTP_STATUS_CODE     StatusCode
 | 
						|
  )
 | 
						|
{
 | 
						|
  //
 | 
						|
  // RFC 2616:
 | 
						|
  // All responses to the HEAD request method
 | 
						|
  // MUST NOT include a message-body, even though the presence of entity-
 | 
						|
  // header fields might lead one to believe they do. All 1xx
 | 
						|
  // (informational), 204 (no content), and 304 (not modified) responses
 | 
						|
  // MUST NOT include a message-body. All other responses do include a
 | 
						|
  // message-body, although it MAY be of zero length.
 | 
						|
  //
 | 
						|
  if (Method == HttpMethodHead) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
 | 
						|
      (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
 | 
						|
      (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
 | 
						|
      (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
 | 
						|
  {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize a HTTP message-body parser.
 | 
						|
 | 
						|
  This function will create and initialize a HTTP message parser according to caller provided HTTP message
 | 
						|
  header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
 | 
						|
 | 
						|
  @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.
 | 
						|
  @param[in]    StatusCode         Response status code returned by the remote host.
 | 
						|
  @param[in]    HeaderCount        Number of HTTP header structures in Headers.
 | 
						|
  @param[in]    Headers            Array containing list of HTTP headers.
 | 
						|
  @param[in]    Callback           Callback function that is invoked when parsing the HTTP message-body,
 | 
						|
                                   set to NULL to ignore all events.
 | 
						|
  @param[in]    Context            Pointer to the context that will be passed to Callback.
 | 
						|
  @param[out]   MsgParser          Pointer to the returned buffer to store the message parser.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              Successfully initialized the parser.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
 | 
						|
  @retval EFI_INVALID_PARAMETER    MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
 | 
						|
  @retval Others                   Failed to initialize the parser.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpInitMsgParser (
 | 
						|
  IN     EFI_HTTP_METHOD               Method,
 | 
						|
  IN     EFI_HTTP_STATUS_CODE          StatusCode,
 | 
						|
  IN     UINTN                         HeaderCount,
 | 
						|
  IN     EFI_HTTP_HEADER               *Headers,
 | 
						|
  IN     HTTP_BODY_PARSER_CALLBACK     Callback,
 | 
						|
  IN     VOID                          *Context,
 | 
						|
    OUT  VOID                          **MsgParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  HTTP_BODY_PARSER      *Parser;
 | 
						|
 | 
						|
  if (HeaderCount != 0 && Headers == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (MsgParser == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
 | 
						|
  if (Parser == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser->State = BodyParserBodyStart;
 | 
						|
 | 
						|
  //
 | 
						|
  // Determine the message length according to RFC 2616.
 | 
						|
  // 1. Check whether the message "MUST NOT" have a message-body.
 | 
						|
  //
 | 
						|
  Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
 | 
						|
  //
 | 
						|
  // 2. Check whether the message using "chunked" transfer-coding.
 | 
						|
  //
 | 
						|
  Parser->IsChunked  = HttpIoIsChunked (HeaderCount, Headers);
 | 
						|
  //
 | 
						|
  // 3. Check whether the message has a Content-Length header field.
 | 
						|
  //
 | 
						|
  Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Parser->ContentLengthIsValid = TRUE;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
 | 
						|
  // 5. By server closing the connection
 | 
						|
  //
 | 
						|
 | 
						|
  //
 | 
						|
  // Set state to skip body parser if the message shouldn't have a message body.
 | 
						|
  //
 | 
						|
  if (Parser->IgnoreBody) {
 | 
						|
    Parser->State = BodyParserComplete;
 | 
						|
  } else {
 | 
						|
    Parser->Callback = Callback;
 | 
						|
    Parser->Context  = Context;
 | 
						|
  }
 | 
						|
 | 
						|
  *MsgParser = Parser;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Parse message body.
 | 
						|
 | 
						|
  Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
 | 
						|
 | 
						|
  @param[in, out]    MsgParser            Pointer to the message parser.
 | 
						|
  @param[in]         BodyLength           Length in bytes of the Body.
 | 
						|
  @param[in]         Body                 Pointer to the buffer of the message-body to be parsed.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS                Successfully parse the message-body.
 | 
						|
  @retval EFI_INVALID_PARAMETER      MsgParser is NULL or Body is NULL or BodyLength is 0.
 | 
						|
  @retval EFI_ABORTED                Operation aborted.
 | 
						|
  @retval Other                      Error happened while parsing message body.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpParseMessageBody (
 | 
						|
  IN OUT VOID              *MsgParser,
 | 
						|
  IN     UINTN             BodyLength,
 | 
						|
  IN     CHAR8             *Body
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8                 *Char;
 | 
						|
  UINTN                 RemainderLengthInThis;
 | 
						|
  UINTN                 LengthForCallback;
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  HTTP_BODY_PARSER      *Parser;
 | 
						|
 | 
						|
  if (BodyLength == 0 || Body == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (MsgParser == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_BODY_PARSER *) MsgParser;
 | 
						|
 | 
						|
  if (Parser->IgnoreBody) {
 | 
						|
    Parser->State = BodyParserComplete;
 | 
						|
    if (Parser->Callback != NULL) {
 | 
						|
      Status = Parser->Callback (
 | 
						|
                         BodyParseEventOnComplete,
 | 
						|
                         Body,
 | 
						|
                         0,
 | 
						|
                         Parser->Context
 | 
						|
                         );
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Parser->State == BodyParserBodyStart) {
 | 
						|
    Parser->ParsedBodyLength = 0;
 | 
						|
    if (Parser->IsChunked) {
 | 
						|
      Parser->State = BodyParserChunkSizeStart;
 | 
						|
    } else {
 | 
						|
      Parser->State = BodyParserBodyIdentity;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
 | 
						|
  //
 | 
						|
  for (Char = Body; Char < Body + BodyLength; ) {
 | 
						|
 | 
						|
    switch (Parser->State) {
 | 
						|
    case BodyParserStateMax:
 | 
						|
      return EFI_ABORTED;
 | 
						|
 | 
						|
    case BodyParserBodyIdentity:
 | 
						|
      //
 | 
						|
      // Identity transfer-coding, just notify user to save the body data.
 | 
						|
      //
 | 
						|
      if (Parser->Callback != NULL) {
 | 
						|
        Status = Parser->Callback (
 | 
						|
                           BodyParseEventOnData,
 | 
						|
                           Char,
 | 
						|
                           MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
 | 
						|
                           Parser->Context
 | 
						|
                           );
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          return Status;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
 | 
						|
      Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
 | 
						|
      if (Parser->ParsedBodyLength == Parser->ContentLength) {
 | 
						|
        Parser->State = BodyParserComplete;
 | 
						|
        if (Parser->Callback != NULL) {
 | 
						|
          Status = Parser->Callback (
 | 
						|
                             BodyParseEventOnComplete,
 | 
						|
                             Char,
 | 
						|
                             0,
 | 
						|
                             Parser->Context
 | 
						|
                             );
 | 
						|
          if (EFI_ERROR (Status)) {
 | 
						|
            return Status;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkSizeStart:
 | 
						|
      //
 | 
						|
      // First byte of chunk-size, the chunk-size might be truncated.
 | 
						|
      //
 | 
						|
      Parser->CurrentChunkSize = 0;
 | 
						|
      Parser->State = BodyParserChunkSize;
 | 
						|
    case BodyParserChunkSize:
 | 
						|
      if (!NET_IS_HEX_CHAR (*Char)) {
 | 
						|
        if (*Char == ';') {
 | 
						|
          Parser->State = BodyParserChunkExtStart;
 | 
						|
          Char++;
 | 
						|
        } else if (*Char == '\r') {
 | 
						|
          Parser->State = BodyParserChunkSizeEndCR;
 | 
						|
          Char++;
 | 
						|
        } else {
 | 
						|
          Parser->State = BodyParserStateMax;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
 | 
						|
        return EFI_INVALID_PARAMETER;
 | 
						|
      }
 | 
						|
      Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
 | 
						|
      Char++;
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkExtStart:
 | 
						|
      //
 | 
						|
      // Ignore all the chunk extensions.
 | 
						|
      //
 | 
						|
      if (*Char == '\r') {
 | 
						|
        Parser->State = BodyParserChunkSizeEndCR;
 | 
						|
       }
 | 
						|
      Char++;
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkSizeEndCR:
 | 
						|
      if (*Char != '\n') {
 | 
						|
        Parser->State = BodyParserStateMax;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      Char++;
 | 
						|
      if (Parser->CurrentChunkSize == 0) {
 | 
						|
        //
 | 
						|
        // The last chunk has been parsed and now assumed the state
 | 
						|
        // of HttpBodyParse is ParserLastCRLF. So it need to decide
 | 
						|
        // whether the rest message is trailer or last CRLF in the next round.
 | 
						|
        //
 | 
						|
        Parser->ContentLengthIsValid = TRUE;
 | 
						|
        Parser->State = BodyParserLastCRLF;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      Parser->State = BodyParserChunkDataStart;
 | 
						|
      Parser->CurrentChunkParsedSize = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserLastCRLF:
 | 
						|
      //
 | 
						|
      // Judge the byte is belong to the Last CRLF or trailer, and then
 | 
						|
      // configure the state of HttpBodyParse to corresponding state.
 | 
						|
      //
 | 
						|
      if (*Char == '\r') {
 | 
						|
        Char++;
 | 
						|
        Parser->State = BodyParserLastCRLFEnd;
 | 
						|
        break;
 | 
						|
      } else {
 | 
						|
        Parser->State = BodyParserTrailer;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
    case BodyParserLastCRLFEnd:
 | 
						|
      if (*Char == '\n') {
 | 
						|
        Parser->State = BodyParserComplete;
 | 
						|
        Char++;
 | 
						|
        if (Parser->Callback != NULL) {
 | 
						|
          Status = Parser->Callback (
 | 
						|
                             BodyParseEventOnComplete,
 | 
						|
                             Char,
 | 
						|
                             0,
 | 
						|
                             Parser->Context
 | 
						|
                             );
 | 
						|
          if (EFI_ERROR (Status)) {
 | 
						|
            return Status;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      } else {
 | 
						|
        Parser->State = BodyParserStateMax;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
    case BodyParserTrailer:
 | 
						|
      if (*Char == '\r') {
 | 
						|
        Parser->State = BodyParserChunkSizeEndCR;
 | 
						|
      }
 | 
						|
      Char++;
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkDataStart:
 | 
						|
      //
 | 
						|
      // First byte of chunk-data, the chunk data also might be truncated.
 | 
						|
      //
 | 
						|
      RemainderLengthInThis = BodyLength - (Char - Body);
 | 
						|
      LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
 | 
						|
      if (Parser->Callback != NULL) {
 | 
						|
        Status = Parser->Callback (
 | 
						|
                           BodyParseEventOnData,
 | 
						|
                           Char,
 | 
						|
                           LengthForCallback,
 | 
						|
                           Parser->Context
 | 
						|
                           );
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          return Status;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      Char += LengthForCallback;
 | 
						|
      Parser->ContentLength += LengthForCallback;
 | 
						|
      Parser->CurrentChunkParsedSize += LengthForCallback;
 | 
						|
      if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
 | 
						|
        Parser->State = BodyParserChunkDataEnd;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkDataEnd:
 | 
						|
      if (*Char == '\r') {
 | 
						|
        Parser->State = BodyParserChunkDataEndCR;
 | 
						|
      } else {
 | 
						|
        Parser->State = BodyParserStateMax;
 | 
						|
      }
 | 
						|
      Char++;
 | 
						|
      break;
 | 
						|
 | 
						|
    case BodyParserChunkDataEndCR:
 | 
						|
      if (*Char != '\n') {
 | 
						|
        Parser->State = BodyParserStateMax;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      Char++;
 | 
						|
      Parser->State = BodyParserChunkSizeStart;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  if (Parser->State == BodyParserStateMax) {
 | 
						|
    return EFI_ABORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether the message-body is complete or not.
 | 
						|
 | 
						|
  @param[in]    MsgParser            Pointer to the message parser.
 | 
						|
 | 
						|
  @retval TRUE                       Message-body is complete.
 | 
						|
  @retval FALSE                      Message-body is not complete.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
EFIAPI
 | 
						|
HttpIsMessageComplete (
 | 
						|
  IN VOID              *MsgParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  HTTP_BODY_PARSER      *Parser;
 | 
						|
 | 
						|
  if (MsgParser == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_BODY_PARSER *) MsgParser;
 | 
						|
 | 
						|
  if (Parser->State == BodyParserComplete) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get the content length of the entity.
 | 
						|
 | 
						|
  Note that in trunk transfer, the entity length is not valid until the whole message body is received.
 | 
						|
 | 
						|
  @param[in]    MsgParser            Pointer to the message parser.
 | 
						|
  @param[out]   ContentLength        Pointer to store the length of the entity.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS                Successfully to get the entity length.
 | 
						|
  @retval EFI_NOT_READY              Entity length is not valid yet.
 | 
						|
  @retval EFI_INVALID_PARAMETER      MsgParser is NULL or ContentLength is NULL.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpGetEntityLength (
 | 
						|
  IN  VOID              *MsgParser,
 | 
						|
  OUT UINTN             *ContentLength
 | 
						|
  )
 | 
						|
{
 | 
						|
  HTTP_BODY_PARSER      *Parser;
 | 
						|
 | 
						|
  if (MsgParser == NULL || ContentLength == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Parser = (HTTP_BODY_PARSER *) MsgParser;
 | 
						|
 | 
						|
  if (!Parser->ContentLengthIsValid) {
 | 
						|
    return EFI_NOT_READY;
 | 
						|
  }
 | 
						|
 | 
						|
  *ContentLength = Parser->ContentLength;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Release the resource of the message parser.
 | 
						|
 | 
						|
  @param[in]    MsgParser            Pointer to the message parser.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
HttpFreeMsgParser (
 | 
						|
  IN  VOID           *MsgParser
 | 
						|
  )
 | 
						|
{
 | 
						|
  FreePool (MsgParser);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get the next string, which is distinguished by specified separator.
 | 
						|
 | 
						|
  @param[in]  String             Pointer to the string.
 | 
						|
  @param[in]  Separator          Specified separator used to distinguish where is the beginning
 | 
						|
                                 of next string.
 | 
						|
 | 
						|
  @return     Pointer to the next string.
 | 
						|
  @return     NULL if not find or String is NULL.
 | 
						|
 | 
						|
**/
 | 
						|
CHAR8 *
 | 
						|
AsciiStrGetNextToken (
 | 
						|
  IN CONST CHAR8 *String,
 | 
						|
  IN       CHAR8 Separator
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR8 *Token;
 | 
						|
 | 
						|
  Token = String;
 | 
						|
  while (TRUE) {
 | 
						|
    if (*Token == 0) {
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
    if (*Token == Separator) {
 | 
						|
      return (CHAR8 *)(Token + 1);
 | 
						|
    }
 | 
						|
    Token++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Set FieldName and FieldValue into specified HttpHeader.
 | 
						|
 | 
						|
  @param[in,out]  HttpHeader      Specified HttpHeader.
 | 
						|
  @param[in]  FieldName           FieldName of this HttpHeader, a NULL terminated ASCII string.
 | 
						|
  @param[in]  FieldValue          FieldValue of this HttpHeader, a NULL terminated ASCII string.
 | 
						|
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The FieldName and FieldValue are set into HttpHeader successfully.
 | 
						|
  @retval EFI_INVALID_PARAMETER   The parameter is invalid.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpSetFieldNameAndValue (
 | 
						|
  IN  OUT   EFI_HTTP_HEADER       *HttpHeader,
 | 
						|
  IN  CONST CHAR8                 *FieldName,
 | 
						|
  IN  CONST CHAR8                 *FieldValue
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                       FieldNameSize;
 | 
						|
  UINTN                       FieldValueSize;
 | 
						|
 | 
						|
  if (HttpHeader == NULL || FieldName == NULL || FieldValue == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (HttpHeader->FieldName != NULL) {
 | 
						|
    FreePool (HttpHeader->FieldName);
 | 
						|
  }
 | 
						|
  if (HttpHeader->FieldValue != NULL) {
 | 
						|
    FreePool (HttpHeader->FieldValue);
 | 
						|
  }
 | 
						|
 | 
						|
  FieldNameSize = AsciiStrSize (FieldName);
 | 
						|
  HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);
 | 
						|
  if (HttpHeader->FieldName == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
  CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);
 | 
						|
  HttpHeader->FieldName[FieldNameSize - 1] = 0;
 | 
						|
 | 
						|
  FieldValueSize = AsciiStrSize (FieldValue);
 | 
						|
  HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);
 | 
						|
  if (HttpHeader->FieldValue == NULL) {
 | 
						|
    FreePool (HttpHeader->FieldName);
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
  CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);
 | 
						|
  HttpHeader->FieldValue[FieldValueSize - 1] = 0;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get one key/value header pair from the raw string.
 | 
						|
 | 
						|
  @param[in]  String             Pointer to the raw string.
 | 
						|
  @param[out] FieldName          Points directly to field name within 'HttpHeader'.
 | 
						|
  @param[out] FieldValue         Points directly to field value within 'HttpHeader'.
 | 
						|
 | 
						|
  @return     Pointer to the next raw string.
 | 
						|
  @return     NULL if no key/value header pair from this raw string.
 | 
						|
 | 
						|
**/
 | 
						|
CHAR8 *
 | 
						|
EFIAPI
 | 
						|
HttpGetFieldNameAndValue (
 | 
						|
  IN     CHAR8   *String,
 | 
						|
     OUT CHAR8   **FieldName,
 | 
						|
     OUT CHAR8   **FieldValue
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8  *FieldNameStr;
 | 
						|
  CHAR8  *FieldValueStr;
 | 
						|
  CHAR8  *StrPtr;
 | 
						|
  CHAR8  *EndofHeader;
 | 
						|
 | 
						|
  if (String == NULL || FieldName == NULL || FieldValue == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  *FieldName    = NULL;
 | 
						|
  *FieldValue   = NULL;
 | 
						|
  FieldNameStr  = NULL;
 | 
						|
  FieldValueStr = NULL;
 | 
						|
  StrPtr        = NULL;
 | 
						|
  EndofHeader   = NULL;
 | 
						|
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether the raw HTTP header string is valid or not.
 | 
						|
  //
 | 
						|
  EndofHeader = AsciiStrStr (String, "\r\n\r\n");
 | 
						|
  if (EndofHeader == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Each header field consists of a name followed by a colon (":") and the field value.
 | 
						|
  //
 | 
						|
  FieldNameStr = String;
 | 
						|
  FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
 | 
						|
  if (FieldValueStr == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Replace ':' with 0
 | 
						|
  //
 | 
						|
  *(FieldValueStr - 1) = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
 | 
						|
  // Note: LWS  = [CRLF] 1*(SP|HT), it can be '\r\n ' or '\r\n\t' or ' ' or '\t'.
 | 
						|
  //       CRLF = '\r\n'.
 | 
						|
  //       SP   = ' '.
 | 
						|
  //       HT   = '\t' (Tab).
 | 
						|
  //
 | 
						|
  while (TRUE) {
 | 
						|
    if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {
 | 
						|
      //
 | 
						|
      // Boundary condition check.
 | 
						|
      //
 | 
						|
      if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) {
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      FieldValueStr ++;
 | 
						|
    } else if (*FieldValueStr == '\r') {
 | 
						|
      //
 | 
						|
      // Boundary condition check.
 | 
						|
      //
 | 
						|
      if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) {
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      if (*(FieldValueStr + 1) == '\n' && (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {
 | 
						|
        FieldValueStr = FieldValueStr + 3;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Header fields can be extended over multiple lines by preceding each extra
 | 
						|
  // line with at least one SP or HT.
 | 
						|
  //
 | 
						|
  StrPtr = FieldValueStr;
 | 
						|
  do {
 | 
						|
    StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
 | 
						|
    if (StrPtr == NULL || *StrPtr != '\n') {
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    StrPtr++;
 | 
						|
  } while (*StrPtr == ' ' || *StrPtr == '\t');
 | 
						|
 | 
						|
  //
 | 
						|
  // Replace '\r' with 0
 | 
						|
  //
 | 
						|
  *(StrPtr - 2) = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get FieldName and FieldValue.
 | 
						|
  //
 | 
						|
  *FieldName = FieldNameStr;
 | 
						|
  *FieldValue = FieldValueStr;
 | 
						|
 | 
						|
  return StrPtr;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free existing HeaderFields.
 | 
						|
 | 
						|
  @param[in]  HeaderFields       Pointer to array of key/value header pairs waitting for free.
 | 
						|
  @param[in]  FieldCount         The number of header pairs in HeaderFields.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
HttpFreeHeaderFields (
 | 
						|
  IN  EFI_HTTP_HEADER  *HeaderFields,
 | 
						|
  IN  UINTN            FieldCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                       Index;
 | 
						|
 | 
						|
  if (HeaderFields != NULL) {
 | 
						|
    for (Index = 0; Index < FieldCount; Index++) {
 | 
						|
      if (HeaderFields[Index].FieldName != NULL) {
 | 
						|
        FreePool (HeaderFields[Index].FieldName);
 | 
						|
      }
 | 
						|
      if (HeaderFields[Index].FieldValue != NULL) {
 | 
						|
        FreePool (HeaderFields[Index].FieldValue);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (HeaderFields);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Generate HTTP request message.
 | 
						|
 | 
						|
  This function will allocate memory for the whole HTTP message and generate a
 | 
						|
  well formatted HTTP Request message in it, include the Request-Line, header
 | 
						|
  fields and also the message body. It is the caller's responsibility to free
 | 
						|
  the buffer returned in *RequestMsg.
 | 
						|
 | 
						|
  @param[in]   Message            Pointer to the EFI_HTTP_MESSAGE structure which
 | 
						|
                                  contains the required information to generate
 | 
						|
                                  the HTTP request message.
 | 
						|
  @param[in]   Url                The URL of a remote host.
 | 
						|
  @param[out]  RequestMsg         Pointer to the created HTTP request message.
 | 
						|
                                  NULL if any error occured.
 | 
						|
  @param[out]  RequestMsgSize     Size of the RequestMsg (in bytes).
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             If HTTP request string was created successfully.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.
 | 
						|
  @retval EFI_INVALID_PARAMETER   The input arguments are invalid.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
HttpGenRequestMessage (
 | 
						|
  IN     CONST EFI_HTTP_MESSAGE        *Message,
 | 
						|
  IN     CONST CHAR8                   *Url,
 | 
						|
     OUT CHAR8                         **RequestMsg,
 | 
						|
     OUT UINTN                         *RequestMsgSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                       Status;
 | 
						|
  UINTN                            StrLength;
 | 
						|
  CHAR8                            *RequestPtr;
 | 
						|
  UINTN                            HttpHdrSize;
 | 
						|
  UINTN                            MsgSize;
 | 
						|
  BOOLEAN                          Success;
 | 
						|
  VOID                             *HttpHdr;
 | 
						|
  EFI_HTTP_HEADER                  **AppendList;
 | 
						|
  UINTN                            Index;
 | 
						|
  EFI_HTTP_UTILITIES_PROTOCOL      *HttpUtilitiesProtocol;
 | 
						|
 | 
						|
  Status                = EFI_SUCCESS;
 | 
						|
  HttpHdrSize           = 0;
 | 
						|
  MsgSize               = 0;
 | 
						|
  Success               = FALSE;
 | 
						|
  HttpHdr               = NULL;
 | 
						|
  AppendList            = NULL;
 | 
						|
  HttpUtilitiesProtocol = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // 1. If we have a Request, we cannot have a NULL Url
 | 
						|
  // 2. If we have a Request, HeaderCount can not be non-zero
 | 
						|
  // 3. If we do not have a Request, HeaderCount should be zero
 | 
						|
  // 4. If we do not have Request and Headers, we need at least a message-body
 | 
						|
  //
 | 
						|
  if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) ||
 | 
						|
      (Message->Data.Request != NULL && Url == NULL) ||
 | 
						|
      (Message->Data.Request != NULL && Message->HeaderCount == 0) ||
 | 
						|
      (Message->Data.Request == NULL && Message->HeaderCount != 0) ||
 | 
						|
      (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Message->HeaderCount != 0) {
 | 
						|
    //
 | 
						|
    // Locate the HTTP_UTILITIES protocol.
 | 
						|
    //
 | 
						|
    Status = gBS->LocateProtocol (
 | 
						|
                    &gEfiHttpUtilitiesProtocolGuid,
 | 
						|
                    NULL,
 | 
						|
                    (VOID **) &HttpUtilitiesProtocol
 | 
						|
                    );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Build AppendList to send into HttpUtilitiesBuild
 | 
						|
    //
 | 
						|
    AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));
 | 
						|
    if (AppendList == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    for(Index = 0; Index < Message->HeaderCount; Index++){
 | 
						|
      AppendList[Index] = &Message->Headers[Index];
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Build raw HTTP Headers
 | 
						|
    //
 | 
						|
    Status = HttpUtilitiesProtocol->Build (
 | 
						|
                                      HttpUtilitiesProtocol,
 | 
						|
                                      0,
 | 
						|
                                      NULL,
 | 
						|
                                      0,
 | 
						|
                                      NULL,
 | 
						|
                                      Message->HeaderCount,
 | 
						|
                                      AppendList,
 | 
						|
                                      &HttpHdrSize,
 | 
						|
                                      &HttpHdr
 | 
						|
                                      );
 | 
						|
 | 
						|
    FreePool (AppendList);
 | 
						|
 | 
						|
    if (EFI_ERROR (Status) || HttpHdr == NULL){
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If we have headers to be sent, account for it.
 | 
						|
  //
 | 
						|
  if (Message->HeaderCount != 0) {
 | 
						|
    MsgSize = HttpHdrSize;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If we have a request line, account for the fields.
 | 
						|
  //
 | 
						|
  if (Message->Data.Request != NULL) {
 | 
						|
    MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  //
 | 
						|
  // If we have a message body to be sent, account for it.
 | 
						|
  //
 | 
						|
  MsgSize += Message->BodyLength;
 | 
						|
 | 
						|
  //
 | 
						|
  // memory for the string that needs to be sent to TCP
 | 
						|
  //
 | 
						|
  *RequestMsg = NULL;
 | 
						|
  *RequestMsg = AllocateZeroPool (MsgSize);
 | 
						|
  if (*RequestMsg == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  RequestPtr = *RequestMsg;
 | 
						|
  //
 | 
						|
  // Construct header request
 | 
						|
  //
 | 
						|
  if (Message->Data.Request != NULL) {
 | 
						|
    switch (Message->Data.Request->Method) {
 | 
						|
    case HttpMethodGet:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_GET) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    case HttpMethodPut:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_PUT) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    case HttpMethodPatch:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_PATCH) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    case HttpMethodPost:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_POST) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    case HttpMethodHead:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_HEAD) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    case HttpMethodDelete:
 | 
						|
      StrLength = sizeof (HTTP_METHOD_DELETE) - 1;
 | 
						|
      CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);
 | 
						|
      RequestPtr += StrLength;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      ASSERT (FALSE);
 | 
						|
      Status = EFI_INVALID_PARAMETER;
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    StrLength = AsciiStrLen(EMPTY_SPACE);
 | 
						|
    CopyMem (RequestPtr, EMPTY_SPACE, StrLength);
 | 
						|
    RequestPtr += StrLength;
 | 
						|
 | 
						|
    StrLength = AsciiStrLen (Url);
 | 
						|
    CopyMem (RequestPtr, Url, StrLength);
 | 
						|
    RequestPtr += StrLength;
 | 
						|
 | 
						|
    StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;
 | 
						|
    CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);
 | 
						|
    RequestPtr += StrLength;
 | 
						|
 | 
						|
    if (HttpHdr != NULL) {
 | 
						|
      //
 | 
						|
      // Construct header
 | 
						|
      //
 | 
						|
      CopyMem (RequestPtr, HttpHdr, HttpHdrSize);
 | 
						|
      RequestPtr += HttpHdrSize;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Construct body
 | 
						|
  //
 | 
						|
  if (Message->Body != NULL) {
 | 
						|
    CopyMem (RequestPtr, Message->Body, Message->BodyLength);
 | 
						|
    RequestPtr += Message->BodyLength;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Done
 | 
						|
  //
 | 
						|
  (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);
 | 
						|
  Success     = TRUE;
 | 
						|
 | 
						|
Exit:
 | 
						|
 | 
						|
  if (!Success) {
 | 
						|
    if (*RequestMsg != NULL) {
 | 
						|
      FreePool (*RequestMsg);
 | 
						|
    }
 | 
						|
    *RequestMsg = NULL;
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  if (HttpHdr != NULL) {
 | 
						|
    FreePool (HttpHdr);
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined
 | 
						|
  in UEFI 2.5 specification.
 | 
						|
 | 
						|
  @param[in]  StatusCode         The status code value in HTTP message.
 | 
						|
 | 
						|
  @return                        Value defined in EFI_HTTP_STATUS_CODE .
 | 
						|
 | 
						|
**/
 | 
						|
EFI_HTTP_STATUS_CODE
 | 
						|
EFIAPI
 | 
						|
HttpMappingToStatusCode (
 | 
						|
  IN UINTN                  StatusCode
 | 
						|
  )
 | 
						|
{
 | 
						|
  switch (StatusCode) {
 | 
						|
  case 100:
 | 
						|
    return HTTP_STATUS_100_CONTINUE;
 | 
						|
  case 101:
 | 
						|
    return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
 | 
						|
  case 200:
 | 
						|
    return HTTP_STATUS_200_OK;
 | 
						|
  case 201:
 | 
						|
    return HTTP_STATUS_201_CREATED;
 | 
						|
  case 202:
 | 
						|
    return HTTP_STATUS_202_ACCEPTED;
 | 
						|
  case 203:
 | 
						|
    return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
 | 
						|
  case 204:
 | 
						|
    return HTTP_STATUS_204_NO_CONTENT;
 | 
						|
  case 205:
 | 
						|
    return HTTP_STATUS_205_RESET_CONTENT;
 | 
						|
  case 206:
 | 
						|
    return HTTP_STATUS_206_PARTIAL_CONTENT;
 | 
						|
  case 300:
 | 
						|
    return HTTP_STATUS_300_MULTIPLE_CHOICES;
 | 
						|
  case 301:
 | 
						|
    return HTTP_STATUS_301_MOVED_PERMANENTLY;
 | 
						|
  case 302:
 | 
						|
    return HTTP_STATUS_302_FOUND;
 | 
						|
  case 303:
 | 
						|
    return HTTP_STATUS_303_SEE_OTHER;
 | 
						|
  case 304:
 | 
						|
    return HTTP_STATUS_304_NOT_MODIFIED;
 | 
						|
  case 305:
 | 
						|
    return HTTP_STATUS_305_USE_PROXY;
 | 
						|
  case 307:
 | 
						|
    return HTTP_STATUS_307_TEMPORARY_REDIRECT;
 | 
						|
  case 308:
 | 
						|
    return HTTP_STATUS_308_PERMANENT_REDIRECT;
 | 
						|
  case 400:
 | 
						|
    return HTTP_STATUS_400_BAD_REQUEST;
 | 
						|
  case 401:
 | 
						|
    return HTTP_STATUS_401_UNAUTHORIZED;
 | 
						|
  case 402:
 | 
						|
    return HTTP_STATUS_402_PAYMENT_REQUIRED;
 | 
						|
  case 403:
 | 
						|
    return HTTP_STATUS_403_FORBIDDEN;
 | 
						|
  case 404:
 | 
						|
    return HTTP_STATUS_404_NOT_FOUND;
 | 
						|
  case 405:
 | 
						|
    return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
 | 
						|
  case 406:
 | 
						|
    return HTTP_STATUS_406_NOT_ACCEPTABLE;
 | 
						|
  case 407:
 | 
						|
    return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
 | 
						|
  case 408:
 | 
						|
    return HTTP_STATUS_408_REQUEST_TIME_OUT;
 | 
						|
  case 409:
 | 
						|
    return HTTP_STATUS_409_CONFLICT;
 | 
						|
  case 410:
 | 
						|
    return HTTP_STATUS_410_GONE;
 | 
						|
  case 411:
 | 
						|
    return HTTP_STATUS_411_LENGTH_REQUIRED;
 | 
						|
  case 412:
 | 
						|
    return HTTP_STATUS_412_PRECONDITION_FAILED;
 | 
						|
  case 413:
 | 
						|
    return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
 | 
						|
  case 414:
 | 
						|
    return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
 | 
						|
  case 415:
 | 
						|
    return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
 | 
						|
  case 416:
 | 
						|
    return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
 | 
						|
  case 417:
 | 
						|
    return HTTP_STATUS_417_EXPECTATION_FAILED;
 | 
						|
  case 500:
 | 
						|
    return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
 | 
						|
  case 501:
 | 
						|
    return HTTP_STATUS_501_NOT_IMPLEMENTED;
 | 
						|
  case 502:
 | 
						|
    return HTTP_STATUS_502_BAD_GATEWAY;
 | 
						|
  case 503:
 | 
						|
    return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
 | 
						|
  case 504:
 | 
						|
    return HTTP_STATUS_504_GATEWAY_TIME_OUT;
 | 
						|
  case 505:
 | 
						|
    return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
 | 
						|
 | 
						|
  default:
 | 
						|
    return HTTP_STATUS_UNSUPPORTED_STATUS;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether header field called FieldName is in DeleteList.
 | 
						|
 | 
						|
  @param[in]  DeleteList        Pointer to array of key/value header pairs.
 | 
						|
  @param[in]  DeleteCount       The number of header pairs.
 | 
						|
  @param[in]  FieldName         Pointer to header field's name.
 | 
						|
 | 
						|
  @return     TRUE if FieldName is not in DeleteList, that means this header field is valid.
 | 
						|
  @return     FALSE if FieldName is in DeleteList, that means this header field is invalid.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
EFIAPI
 | 
						|
HttpIsValidHttpHeader (
 | 
						|
  IN  CHAR8            *DeleteList[],
 | 
						|
  IN  UINTN            DeleteCount,
 | 
						|
  IN  CHAR8            *FieldName
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                       Index;
 | 
						|
 | 
						|
  if (FieldName == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < DeleteCount; Index++) {
 | 
						|
    if (DeleteList[Index] == NULL) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 |