mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-10 14:23:31 +01:00
2268 lines
62 KiB
C
2268 lines
62 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 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#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 (EFI_ERROR (Status) || (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 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 + AsciiCharToUpper (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;
|
|
UINTN PortionLength;
|
|
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.
|
|
//
|
|
PortionLength = MIN (
|
|
BodyLength,
|
|
Parser->ContentLength - Parser->ParsedBodyLength
|
|
);
|
|
if (PortionLength == 0) {
|
|
//
|
|
// Got BodyLength, but no ContentLength. Use BodyLength.
|
|
//
|
|
PortionLength = BodyLength;
|
|
Parser->ContentLength = PortionLength;
|
|
}
|
|
|
|
if (Parser->Callback != NULL) {
|
|
Status = Parser->Callback (
|
|
BodyParseEventOnData,
|
|
Char,
|
|
PortionLength,
|
|
Parser->Context
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Char += PortionLength;
|
|
Parser->ParsedBodyLength += PortionLength;
|
|
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.
|
|
// The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
|
|
//
|
|
// message-header = field-name ":" [ field-value ]
|
|
// field-name = token
|
|
// field-value = *( field-content | LWS )
|
|
//
|
|
// Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.
|
|
// [element] means element is optional.
|
|
// LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.
|
|
// CRLF = '\r\n'.
|
|
// SP = ' '.
|
|
// HT = '\t' (Tab).
|
|
//
|
|
FieldNameStr = String;
|
|
FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
|
|
if (FieldValueStr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Replace ':' with 0, then FieldName has been retrived from String.
|
|
//
|
|
*(FieldValueStr - 1) = 0;
|
|
|
|
//
|
|
// Handle FieldValueStr, skip all the preceded LWS.
|
|
//
|
|
while (TRUE) {
|
|
if ((*FieldValueStr == ' ') || (*FieldValueStr == '\t')) {
|
|
//
|
|
// Boundary condition check.
|
|
//
|
|
if ((UINTN)EndofHeader - (UINTN)FieldValueStr < 1) {
|
|
//
|
|
// Wrong String format!
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
FieldValueStr++;
|
|
} else if (*FieldValueStr == '\r') {
|
|
//
|
|
// Boundary condition check.
|
|
//
|
|
if ((UINTN)EndofHeader - (UINTN)FieldValueStr < 3) {
|
|
//
|
|
// No more preceded LWS, so break here.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (*(FieldValueStr + 1) == '\n' ) {
|
|
if ((*(FieldValueStr + 2) == ' ') || (*(FieldValueStr + 2) == '\t')) {
|
|
FieldValueStr = FieldValueStr + 3;
|
|
} else {
|
|
//
|
|
// No more preceded LWS, so break here.
|
|
//
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// Wrong String format!
|
|
//
|
|
return NULL;
|
|
}
|
|
} else {
|
|
//
|
|
// No more preceded LWS, so break here.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
StrPtr = FieldValueStr;
|
|
do {
|
|
//
|
|
// Handle the LWS within the field value.
|
|
//
|
|
StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
|
|
if ((StrPtr == NULL) || (*StrPtr != '\n')) {
|
|
//
|
|
// Wrong String format!
|
|
//
|
|
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 waiting 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 occurred.
|
|
@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;
|
|
}
|
|
|
|
/**
|
|
Create a HTTP_IO_HEADER to hold the HTTP header items.
|
|
|
|
@param[in] MaxHeaderCount The maximun number of HTTP header in this holder.
|
|
|
|
@return A pointer of the HTTP header holder or NULL if failed.
|
|
|
|
**/
|
|
HTTP_IO_HEADER *
|
|
HttpIoCreateHeader (
|
|
UINTN MaxHeaderCount
|
|
)
|
|
{
|
|
HTTP_IO_HEADER *HttpIoHeader;
|
|
|
|
if (MaxHeaderCount == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));
|
|
if (HttpIoHeader == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
HttpIoHeader->MaxHeaderCount = MaxHeaderCount;
|
|
HttpIoHeader->Headers = (EFI_HTTP_HEADER *)(HttpIoHeader + 1);
|
|
|
|
return HttpIoHeader;
|
|
}
|
|
|
|
/**
|
|
Destroy the HTTP_IO_HEADER and release the resources.
|
|
|
|
@param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.
|
|
|
|
**/
|
|
VOID
|
|
HttpIoFreeHeader (
|
|
IN HTTP_IO_HEADER *HttpIoHeader
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
if (HttpIoHeader != NULL) {
|
|
if (HttpIoHeader->HeaderCount != 0) {
|
|
for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {
|
|
FreePool (HttpIoHeader->Headers[Index].FieldName);
|
|
ZeroMem (HttpIoHeader->Headers[Index].FieldValue, AsciiStrSize (HttpIoHeader->Headers[Index].FieldValue));
|
|
FreePool (HttpIoHeader->Headers[Index].FieldValue);
|
|
}
|
|
}
|
|
|
|
FreePool (HttpIoHeader);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set or update a HTTP header with the field name and corresponding value.
|
|
|
|
@param[in] HttpIoHeader Point to the HTTP header holder.
|
|
@param[in] FieldName Null terminated string which describes a field name.
|
|
@param[in] FieldValue Null terminated string which describes the corresponding field value.
|
|
|
|
@retval EFI_SUCCESS The HTTP header has been set or updated.
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.
|
|
@retval Other Unexpected error happened.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
HttpIoSetHeader (
|
|
IN HTTP_IO_HEADER *HttpIoHeader,
|
|
IN CHAR8 *FieldName,
|
|
IN CHAR8 *FieldValue
|
|
)
|
|
{
|
|
EFI_HTTP_HEADER *Header;
|
|
UINTN StrSize;
|
|
CHAR8 *NewFieldValue;
|
|
|
|
if ((HttpIoHeader == NULL) || (FieldName == NULL) || (FieldValue == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Header = HttpFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);
|
|
if (Header == NULL) {
|
|
//
|
|
// Add a new header.
|
|
//
|
|
if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];
|
|
|
|
StrSize = AsciiStrSize (FieldName);
|
|
Header->FieldName = AllocatePool (StrSize);
|
|
if (Header->FieldName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (Header->FieldName, FieldName, StrSize);
|
|
Header->FieldName[StrSize -1] = '\0';
|
|
|
|
StrSize = AsciiStrSize (FieldValue);
|
|
Header->FieldValue = AllocatePool (StrSize);
|
|
if (Header->FieldValue == NULL) {
|
|
FreePool (Header->FieldName);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (Header->FieldValue, FieldValue, StrSize);
|
|
Header->FieldValue[StrSize -1] = '\0';
|
|
|
|
HttpIoHeader->HeaderCount++;
|
|
} else {
|
|
//
|
|
// Update an existing one.
|
|
//
|
|
StrSize = AsciiStrSize (FieldValue);
|
|
NewFieldValue = AllocatePool (StrSize);
|
|
if (NewFieldValue == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (NewFieldValue, FieldValue, StrSize);
|
|
NewFieldValue[StrSize -1] = '\0';
|
|
|
|
if (Header->FieldValue != NULL) {
|
|
FreePool (Header->FieldValue);
|
|
}
|
|
|
|
Header->FieldValue = NewFieldValue;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|