mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-30 23:50:23 +00:00 
			
		
		
		
	 8a1c4a8cf9
			
		
	
	
		8a1c4a8cf9
		
	
	
	
	
		
			
			Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19597 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1428 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1428 lines
		
	
	
		
			39 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, Intel Corporation. All rights reserved.<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 <Uefi.h>
 | |
| #include <Library/NetLib.h>
 | |
| #include <Library/HttpLib.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/MemoryAllocationLib.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| 
 | |
| #define BIT(x)  (1 << x)
 | |
| 
 | |
| #define NET_IS_HEX_CHAR(Ch)   \
 | |
|   ((('0' <= (Ch)) && ((Ch) <= '9')) ||  \
 | |
|    (('A' <= (Ch)) && ((Ch) <= 'F')) ||  \
 | |
|    (('a' <= (Ch)) && ((Ch) <= 'f')))
 | |
| 
 | |
| //
 | |
| // Field index of the HTTP URL parse result.
 | |
| //
 | |
| #define   HTTP_URI_FIELD_SCHEME           0
 | |
| #define   HTTP_URI_FIELD_AUTHORITY        1
 | |
| #define   HTTP_URI_FIELD_PATH             2
 | |
| #define   HTTP_URI_FIELD_QUERY            3
 | |
| #define   HTTP_URI_FIELD_FRAGMENT         4
 | |
| #define   HTTP_URI_FIELD_USERINFO         5
 | |
| #define   HTTP_URI_FIELD_HOST             6
 | |
| #define   HTTP_URI_FIELD_PORT             7
 | |
| #define   HTTP_URI_FIELD_MAX              8
 | |
| 
 | |
| //
 | |
| // Structure to store the parse result of a HTTP URL.
 | |
| //
 | |
| typedef struct {
 | |
|     UINT32      Offset;
 | |
|     UINT32      Length;
 | |
| } HTTP_URL_FILED_DATA;
 | |
| 
 | |
| typedef struct {
 | |
|   UINT16                  FieldBitMap;
 | |
|   HTTP_URL_FILED_DATA     FieldData[HTTP_URI_FIELD_MAX];
 | |
| } HTTP_URL_PARSER;
 | |
| 
 | |
| typedef enum {
 | |
|   UrlParserUrlStart,
 | |
|   UrlParserScheme,
 | |
|   UrlParserSchemeColon,            // ":"
 | |
|   UrlParserSchemeColonSlash,       // ":/"
 | |
|   UrlParserSchemeColonSlashSlash,  // "://"
 | |
|   UrlParserAuthority,
 | |
|   UrlParserAtInAuthority,
 | |
|   UrlParserPath,
 | |
|   UrlParserQueryStart,    // "?"
 | |
|   UrlParserQuery,
 | |
|   UrlParserFragmentStart, // "#"
 | |
|   UrlParserFragment,
 | |
|   UrlParserUserInfo,
 | |
|   UrlParserHostStart,     // "@"
 | |
|   UrlParserHost,
 | |
|   UrlParserHostIpv6,      // "["(Ipv6 address) "]"
 | |
|   UrlParserPortStart,     // ":"
 | |
|   UrlParserPort,
 | |
|   UrlParserStateMax
 | |
| } HTTP_URL_PARSE_STATE;
 | |
| 
 | |
| /**
 | |
|   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 (!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 Other                   Error happened.
 | |
| 
 | |
| **/
 | |
| 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;
 | |
|   
 | |
|   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 accoring to next char.
 | |
|     //
 | |
|     State = NetHttpParseUrlChar (*Char, State);
 | |
| 
 | |
|     switch (State) {
 | |
|     case UrlParserStateMax:
 | |
|       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)) {
 | |
|       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)) {
 | |
|     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_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   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)) {
 | |
|     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_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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)) {
 | |
|     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;
 | |
|   UINT32        ResultLength;
 | |
|   HTTP_URL_PARSER      *Parser;
 | |
| 
 | |
|   if (Url == NULL || UrlParser == NULL || Port == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Parser = (HTTP_URL_PARSER*) UrlParser;
 | |
| 
 | |
|   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   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)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   PortString[ResultLength] = '\0';
 | |
|   *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset);
 | |
| 
 | |
|   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 *
 | |
| HttpIoFindHeader (
 | |
|   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 = HttpIoFindHeader (HeaderCount, Headers, "Content-Length");
 | |
|   if (Header == NULL) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue);
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
| 
 | |
|   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 = HttpIoFindHeader (HeaderCount, Headers, "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 Others                     Operation aborted.
 | |
| 
 | |
| **/
 | |
| 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;
 | |
| 
 | |
|   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);
 | |
| }
 |