This library is used to share code between UEFI network stack modules.\r
It provides the helper routines to parse the HTTP message byte stream.\r
\r
-Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>\r
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at<BR>\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
\r
/**\r
Decode a percent-encoded URI component to the ASCII character.\r
- \r
- Decode the input component in Buffer according to RFC 3986. The caller is responsible to make \r
+\r
+ Decode the input component in Buffer according to RFC 3986. The caller is responsible to make\r
sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))\r
- in bytes. \r
+ in bytes.\r
\r
@param[in] Buffer The pointer to a percent-encoded URI component.\r
@param[in] BufferLength Length of Buffer in bytes.\r
\r
@retval EFI_SUCCESS Successfully decoded the URI.\r
@retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
- \r
+\r
Index = 0;\r
Offset = 0;\r
HexStr[2] = '\0';\r
while (Index < BufferLength) {\r
if (Buffer[Index] == '%') {\r
- if (Index + 1 >= BufferLength || Index + 2 >= BufferLength || \r
+ if (Index + 1 >= BufferLength || Index + 2 >= BufferLength ||\r
!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {\r
return EFI_INVALID_PARAMETER;\r
}\r
}\r
\r
*ResultLength = (UINT32) Offset;\r
- \r
+\r
return EFI_SUCCESS;\r
}\r
\r
\r
@param[in] Char Next character.\r
@param[in] State Current value of the parser state machine.\r
- @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and \r
- indicates the next part is ':' before Port. \r
+ @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and\r
+ indicates the next part is ':' before Port.\r
\r
@return Updated state value.\r
**/\r
break;\r
\r
case UrlParserHost:\r
- case UrlParserHostStart: \r
+ case UrlParserHostStart:\r
if (Char == '[') {\r
return UrlParserHostIpv6;\r
}\r
- \r
+\r
if (Char == ':') {\r
return UrlParserPortStart;\r
}\r
- \r
+\r
return UrlParserHost;\r
- \r
- case UrlParserHostIpv6: \r
+\r
+ case UrlParserHostIpv6:\r
if (Char == ']') {\r
*IsRightBracket = TRUE;\r
}\r
- \r
+\r
if (Char == ':' && *IsRightBracket) {\r
return UrlParserPortStart;\r
}\r
return UrlParserHostIpv6;\r
- \r
+\r
case UrlParserPort:\r
case UrlParserPortStart:\r
return UrlParserPort;\r
UINT32 Field;\r
UINT32 OldField;\r
BOOLEAN IsrightBracket;\r
- \r
+\r
ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);\r
\r
//\r
case UrlParserUserInfo:\r
Field = HTTP_URI_FIELD_USERINFO;\r
break;\r
- \r
+\r
case UrlParserHost:\r
Field = HTTP_URI_FIELD_HOST;\r
break;\r
case UrlParserHostIpv6:\r
Field = HTTP_URI_FIELD_HOST;\r
break;\r
- \r
+\r
case UrlParserPort:\r
Field = HTTP_URI_FIELD_PORT;\r
break;\r
if (Char == ' ' || Char == '\r' || Char == '\n') {\r
return UrlParserStateMax;\r
}\r
- \r
+\r
//\r
// http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]\r
- // \r
+ //\r
// Request-URI = "*" | absolute-URI | path-absolute | authority\r
- // \r
+ //\r
// absolute-URI = scheme ":" hier-part [ "?" query ]\r
// path-absolute = "/" [ segment-nz *( "/" segment ) ]\r
// authority = [ userinfo "@" ] host [ ":" port ]\r
\r
case UrlParserFragmentStart:\r
return UrlParserFragment;\r
- \r
+\r
default:\r
break;\r
}\r
HTTP_URL_PARSER *Parser;\r
\r
Parser = NULL;\r
- \r
+\r
if (Url == NULL || Length == 0 || UrlParser == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
if (Parser == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- \r
+\r
if (IsConnectMethod) {\r
//\r
// According to RFC 2616, the authority form is only used by the CONNECT method.\r
case UrlParserStateMax:\r
FreePool (Parser);\r
return EFI_INVALID_PARAMETER;\r
- \r
+\r
case UrlParserSchemeColon:\r
case UrlParserSchemeColonSlash:\r
case UrlParserSchemeColonSlashSlash:\r
// Skip all the delimiting char: "://" "?" "@"\r
//\r
continue;\r
- \r
+\r
case UrlParserScheme:\r
Field = HTTP_URI_FIELD_SCHEME;\r
break;\r
}\r
\r
*UrlParser = Parser;\r
- return EFI_SUCCESS; \r
+ return EFI_SUCCESS;\r
}\r
\r
/**\r
@retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.\r
@retval EFI_NOT_FOUND No hostName component in the URL.\r
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
if (Name == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- \r
+\r
Status = UriPercentDecode (\r
Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
@retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.\r
@retval EFI_NOT_FOUND No IPv4 address component in the URL.\r
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
EFI_STATUS Status;\r
UINT32 ResultLength;\r
HTTP_URL_PARSER *Parser;\r
- \r
+\r
if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
if (Ip4String == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- \r
+\r
Status = UriPercentDecode (\r
Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
@retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.\r
@retval EFI_NOT_FOUND No IPv6 address component in the URL.\r
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
EFI_STATUS Status;\r
UINT32 ResultLength;\r
HTTP_URL_PARSER *Parser;\r
- \r
+\r
if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
if (Ip6String == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- \r
+\r
Status = UriPercentDecode (\r
Ptr + 1,\r
Length - 2,\r
FreePool (Ip6String);\r
return Status;\r
}\r
- \r
+\r
Ip6String[ResultLength] = '\0';\r
Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);\r
FreePool (Ip6String);\r
@retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.\r
@retval EFI_NOT_FOUND No port number in the URL.\r
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
@retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.\r
@retval EFI_NOT_FOUND No hostName component in the URL.\r
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
if (PathStr == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- \r
+\r
Status = UriPercentDecode (\r
Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,\r
Parser->FieldData[HTTP_URI_FIELD_PATH].Length,\r
Release the resource of the URL parser.\r
\r
@param[in] UrlParser Pointer to the parser.\r
- \r
+\r
**/\r
VOID\r
EFIAPI\r
/**\r
Find a specified header field according to the field name.\r
\r
- @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.\r
@param[in] Headers Array containing list of HTTP headers.\r
- @param[in] FieldName Null terminated string which describes a field name. \r
+ @param[in] FieldName Null terminated string which describes a field name.\r
\r
@return Pointer to the found header or NULL.\r
\r
UINTN CurrentChunkParsedSize;\r
} HTTP_BODY_PARSER;\r
\r
-/**\r
-\r
- Convert an Ascii char to its uppercase.\r
-\r
- @param[in] Char Ascii character.\r
-\r
- @return Uppercase value of the input Char.\r
-\r
-**/\r
-CHAR8\r
-HttpIoCharToUpper (\r
- IN CHAR8 Char\r
- )\r
-{\r
- if (Char >= 'a' && Char <= 'z') {\r
- return Char - ('a' - 'A');\r
- }\r
-\r
- return Char;\r
-}\r
-\r
/**\r
Convert an hexadecimal char to a value of type UINTN.\r
\r
return Char - '0';\r
}\r
\r
- return (10 + HttpIoCharToUpper (Char) - 'A');\r
+ return (10 + AsciiCharToUpper (Char) - 'A');\r
}\r
\r
/**\r
@param[in] Headers Array containing list of HTTP headers.\r
\r
@return The message is "chunked" transfer-coding (TRUE) or not (FALSE).\r
- \r
+\r
**/\r
BOOLEAN\r
HttpIoIsChunked (\r
{\r
EFI_STATUS Status;\r
HTTP_BODY_PARSER *Parser;\r
- \r
+\r
if (HeaderCount != 0 && Headers == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
}\r
\r
Parser->State = BodyParserBodyStart;\r
- \r
+\r
//\r
// Determine the message length according to RFC 2616.\r
// 1. Check whether the message "MUST NOT" have a message-body.\r
// 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".\r
// 5. By server closing the connection\r
//\r
- \r
+\r
//\r
// Set state to skip body parser if the message shouldn't have a message body.\r
//\r
UINTN LengthForCallback;\r
EFI_STATUS Status;\r
HTTP_BODY_PARSER *Parser;\r
- \r
+\r
if (BodyLength == 0 || Body == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
switch (Parser->State) {\r
case BodyParserStateMax:\r
return EFI_ABORTED;\r
- \r
+\r
case BodyParserBodyIdentity:\r
//\r
// Identity transfer-coding, just notify user to save the body data.\r
}\r
Char++;\r
break;\r
- \r
+\r
case BodyParserChunkSizeEndCR:\r
if (*Char != '\n') {\r
Parser->State = BodyParserStateMax;\r
Char++;\r
if (Parser->CurrentChunkSize == 0) {\r
//\r
- // The last chunk has been parsed and now assumed the state \r
+ // The last chunk has been parsed and now assumed the state\r
// of HttpBodyParse is ParserLastCRLF. So it need to decide\r
// whether the rest message is trailer or last CRLF in the next round.\r
//\r
Parser->State = BodyParserChunkDataStart;\r
Parser->CurrentChunkParsedSize = 0;\r
break;\r
- \r
+\r
case BodyParserLastCRLF:\r
//\r
- // Judge the byte is belong to the Last CRLF or trailer, and then \r
+ // Judge the byte is belong to the Last CRLF or trailer, and then\r
// configure the state of HttpBodyParse to corresponding state.\r
//\r
if (*Char == '\r') {\r
Parser->State = BodyParserTrailer;\r
break;\r
}\r
- \r
+\r
case BodyParserLastCRLFEnd:\r
if (*Char == '\n') {\r
Parser->State = BodyParserComplete;\r
Parser->State = BodyParserStateMax;\r
break;\r
}\r
- \r
+\r
case BodyParserTrailer:\r
if (*Char == '\r') {\r
Parser->State = BodyParserChunkSizeEndCR;\r
}\r
Char++;\r
- break; \r
+ break;\r
\r
case BodyParserChunkDataStart:\r
//\r
Parser->CurrentChunkParsedSize += LengthForCallback;\r
if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {\r
Parser->State = BodyParserChunkDataEnd;\r
- } \r
+ }\r
break;\r
\r
case BodyParserChunkDataEnd:\r
}\r
Char++;\r
Parser->State = BodyParserChunkSizeStart;\r
- break; \r
+ break;\r
\r
default:\r
break;\r
@retval EFI_SUCCESS Successfully to get the entity length.\r
@retval EFI_NOT_READY Entity length is not valid yet.\r
@retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.\r
- \r
+\r
**/\r
EFI_STATUS\r
EFIAPI\r
Release the resource of the message parser.\r
\r
@param[in] MsgParser Pointer to the message parser.\r
- \r
+\r
**/\r
VOID\r
EFIAPI\r
\r
//\r
// Each header field consists of a name followed by a colon (":") and the field value.\r
+ // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
+ //\r
+ // message-header = field-name ":" [ field-value ]\r
+ // field-name = token\r
+ // field-value = *( field-content | LWS )\r
+ //\r
+ // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.\r
+ // [element] means element is optional.\r
+ // LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.\r
+ // CRLF = '\r\n'.\r
+ // SP = ' '.\r
+ // HT = '\t' (Tab).\r
//\r
FieldNameStr = String;\r
FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');\r
}\r
\r
//\r
- // Replace ':' with 0\r
+ // Replace ':' with 0, then FieldName has been retrived from String.\r
//\r
*(FieldValueStr - 1) = 0;\r
\r
//\r
- // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
- // Note: LWS = [CRLF] 1*(SP|HT), it can be '\r\n ' or '\r\n\t' or ' ' or '\t'.\r
- // CRLF = '\r\n'.\r
- // SP = ' '.\r
- // HT = '\t' (Tab).\r
+ // Handle FieldValueStr, skip all the preceded LWS.\r
//\r
while (TRUE) {\r
if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {\r
//\r
- // Boundary condition check. \r
+ // Boundary condition check.\r
//\r
if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) {\r
- return NULL; \r
+ //\r
+ // Wrong String format!\r
+ //\r
+ return NULL;\r
}\r
- \r
+\r
FieldValueStr ++;\r
} else if (*FieldValueStr == '\r') {\r
//\r
- // Boundary condition check. \r
+ // Boundary condition check.\r
//\r
if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) {\r
- return NULL; \r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
+ break;\r
}\r
\r
- if (*(FieldValueStr + 1) == '\n' && (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {\r
- FieldValueStr = FieldValueStr + 3;\r
+ if (*(FieldValueStr + 1) == '\n' ) {\r
+ if (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t') {\r
+ FieldValueStr = FieldValueStr + 3;\r
+ } else {\r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
+ break;\r
+ }\r
+ } else {\r
+ //\r
+ // Wrong String format!\r
+ //\r
+ return NULL;\r
}\r
} else {\r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
break;\r
}\r
}\r
\r
- //\r
- // Header fields can be extended over multiple lines by preceding each extra\r
- // line with at least one SP or HT.\r
- //\r
StrPtr = FieldValueStr;\r
do {\r
+ //\r
+ // Handle the LWS within the field value.\r
+ //\r
StrPtr = AsciiStrGetNextToken (StrPtr, '\r');\r
if (StrPtr == NULL || *StrPtr != '\n') {\r
+ //\r
+ // Wrong String format!\r
+ //\r
return NULL;\r
}\r
\r
// 3. If we do not have a Request, HeaderCount should be zero\r
// 4. If we do not have Request and Headers, we need at least a message-body\r
//\r
- if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) || \r
+ if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) ||\r
(Message->Data.Request != NULL && Url == NULL) ||\r
(Message->Data.Request != NULL && Message->HeaderCount == 0) ||\r
(Message->Data.Request == NULL && Message->HeaderCount != 0) ||\r
if (DeleteList[Index] == NULL) {\r
continue;\r
}\r
- \r
+\r
if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {\r
return FALSE;\r
}\r