2 This library is used to share code between UEFI network stack modules.
3 It provides the helper routines to parse the HTTP message byte stream.
5 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at<BR>
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/NetLib.h>
18 #include <Library/HttpLib.h>
19 #include <Library/BaseLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
24 #define BIT(x) (1 << x)
26 #define NET_IS_HEX_CHAR(Ch) \
27 ((('0' <= (Ch)) && ((Ch) <= '9')) || \
28 (('A' <= (Ch)) && ((Ch) <= 'F')) || \
29 (('a' <= (Ch)) && ((Ch) <= 'f')))
32 // Field index of the HTTP URL parse result.
34 #define HTTP_URI_FIELD_SCHEME 0
35 #define HTTP_URI_FIELD_AUTHORITY 1
36 #define HTTP_URI_FIELD_PATH 2
37 #define HTTP_URI_FIELD_QUERY 3
38 #define HTTP_URI_FIELD_FRAGMENT 4
39 #define HTTP_URI_FIELD_USERINFO 5
40 #define HTTP_URI_FIELD_HOST 6
41 #define HTTP_URI_FIELD_PORT 7
42 #define HTTP_URI_FIELD_MAX 8
45 // Structure to store the parse result of a HTTP URL.
50 } HTTP_URL_FILED_DATA
;
54 HTTP_URL_FILED_DATA FieldData
[HTTP_URI_FIELD_MAX
];
60 UrlParserSchemeColon
, // ":"
61 UrlParserSchemeColonSlash
, // ":/"
62 UrlParserSchemeColonSlashSlash
, // "://"
64 UrlParserAtInAuthority
,
66 UrlParserQueryStart
, // "?"
68 UrlParserFragmentStart
, // "#"
71 UrlParserHostStart
, // "@"
73 UrlParserPortStart
, // ":"
76 } HTTP_URL_PARSE_STATE
;
79 Decode a percent-encoded URI component to the ASCII character.
81 Decode the input component in Buffer according to RFC 3986. The caller is responsible to make
82 sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))
85 @param[in] Buffer The pointer to a percent-encoded URI component.
86 @param[in] BufferLength Length of Buffer in bytes.
87 @param[out] ResultBuffer Point to the buffer to store the decode result.
88 @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.
90 @retval EFI_SUCCESS Successfully decoded the URI.
91 @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.
98 IN UINT32 BufferLength
,
99 OUT CHAR8
*ResultBuffer
,
100 OUT UINT32
*ResultLength
107 if (Buffer
== NULL
|| BufferLength
== 0 || ResultBuffer
== NULL
) {
108 return EFI_INVALID_PARAMETER
;
114 while (Index
< BufferLength
) {
115 if (Buffer
[Index
] == '%') {
116 if (!NET_IS_HEX_CHAR (Buffer
[Index
+1]) || !NET_IS_HEX_CHAR (Buffer
[Index
+2])) {
117 return EFI_INVALID_PARAMETER
;
119 HexStr
[0] = Buffer
[Index
+1];
120 HexStr
[1] = Buffer
[Index
+2];
121 ResultBuffer
[Offset
] = (CHAR8
) AsciiStrHexToUintn (HexStr
);
124 ResultBuffer
[Offset
] = Buffer
[Index
];
130 *ResultLength
= (UINT32
) Offset
;
136 This function return the updated state accroding to the input state and next character of
139 @param[in] Char Next character.
140 @param[in] State Current value of the parser state machine.
142 @return Updated state value.
145 NetHttpParseAuthorityChar (
147 IN HTTP_URL_PARSE_STATE State
153 // The authority component is preceded by a double slash ("//") and is
154 // terminated by the next slash ("/"), question mark ("?"), or number
155 // sign ("#") character, or by the end of the URI.
157 if (Char
== ' ' || Char
== '\r' || Char
== '\n') {
158 return UrlParserStateMax
;
162 // authority = [ userinfo "@" ] host [ ":" port ]
165 case UrlParserUserInfo
:
167 return UrlParserHostStart
;
172 case UrlParserHostStart
:
174 return UrlParserPortStart
;
176 return UrlParserHost
;
179 case UrlParserPortStart
:
180 return UrlParserPort
;
190 This function parse the authority component of the input URL and update the parser.
192 @param[in] Url The pointer to a HTTP URL string.
193 @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.
194 @param[in, out] UrlParser Pointer to the buffer of the parse result.
196 @retval EFI_SUCCESS Successfully parse the authority.
197 @retval Other Error happened.
201 NetHttpParseAuthority (
204 IN OUT HTTP_URL_PARSER
*UrlParser
210 HTTP_URL_PARSE_STATE State
;
214 ASSERT ((UrlParser
->FieldBitMap
& BIT (HTTP_URI_FIELD_AUTHORITY
)) != 0);
217 // authority = [ userinfo "@" ] host [ ":" port ]
220 State
= UrlParserUserInfo
;
222 State
= UrlParserHost
;
225 Field
= HTTP_URI_FIELD_MAX
;
227 Authority
= Url
+ UrlParser
->FieldData
[HTTP_URI_FIELD_AUTHORITY
].Offset
;
228 Length
= UrlParser
->FieldData
[HTTP_URI_FIELD_AUTHORITY
].Length
;
229 for (Char
= Authority
; Char
< Authority
+ Length
; Char
++) {
230 State
= NetHttpParseAuthorityChar (*Char
, State
);
232 case UrlParserStateMax
:
233 return EFI_INVALID_PARAMETER
;
235 case UrlParserHostStart
:
236 case UrlParserPortStart
:
239 case UrlParserUserInfo
:
240 Field
= HTTP_URI_FIELD_USERINFO
;
244 Field
= HTTP_URI_FIELD_HOST
;
248 Field
= HTTP_URI_FIELD_PORT
;
256 // Field not changed, count the length.
258 ASSERT (Field
< HTTP_URI_FIELD_MAX
);
259 if (Field
== OldField
) {
260 UrlParser
->FieldData
[Field
].Length
++;
267 UrlParser
->FieldBitMap
|= BIT (Field
);
268 UrlParser
->FieldData
[Field
].Offset
= (UINT32
) (Char
- Url
);
269 UrlParser
->FieldData
[Field
].Length
= 1;
277 This function return the updated state accroding to the input state and next character of a URL.
279 @param[in] Char Next character.
280 @param[in] State Current value of the parser state machine.
282 @return Updated state value.
286 NetHttpParseUrlChar (
288 IN HTTP_URL_PARSE_STATE State
291 if (Char
== ' ' || Char
== '\r' || Char
== '\n') {
292 return UrlParserStateMax
;
296 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
298 // Request-URI = "*" | absolute-URI | path-absolute | authority
300 // absolute-URI = scheme ":" hier-part [ "?" query ]
301 // path-absolute = "/" [ segment-nz *( "/" segment ) ]
302 // authority = [ userinfo "@" ] host [ ":" port ]
305 case UrlParserUrlStart
:
306 if (Char
== '*' || Char
== '/') {
307 return UrlParserPath
;
309 return UrlParserScheme
;
311 case UrlParserScheme
:
313 return UrlParserSchemeColon
;
317 case UrlParserSchemeColon
:
319 return UrlParserSchemeColonSlash
;
323 case UrlParserSchemeColonSlash
:
325 return UrlParserSchemeColonSlashSlash
;
329 case UrlParserAtInAuthority
:
331 return UrlParserStateMax
;
334 case UrlParserAuthority
:
335 case UrlParserSchemeColonSlashSlash
:
337 return UrlParserAtInAuthority
;
340 return UrlParserPath
;
343 return UrlParserQueryStart
;
346 return UrlParserFragmentStart
;
348 return UrlParserAuthority
;
352 return UrlParserQueryStart
;
355 return UrlParserFragmentStart
;
360 case UrlParserQueryStart
:
362 return UrlParserFragmentStart
;
364 return UrlParserQuery
;
366 case UrlParserFragmentStart
:
367 return UrlParserFragment
;
376 Create a URL parser for the input URL string.
378 This function will parse and dereference the input HTTP URL into it components. The original
379 content of the URL won't be modified and the result will be returned in UrlParser, which can
380 be used in other functions like NetHttpUrlGetHostName().
382 @param[in] Url The pointer to a HTTP URL string.
383 @param[in] Length Length of Url in bytes.
384 @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.
385 @param[out] UrlParser Pointer to the returned buffer to store the parse result.
387 @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.
388 @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.
389 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
397 IN BOOLEAN IsConnectMethod
,
401 HTTP_URL_PARSE_STATE State
;
407 HTTP_URL_PARSER
*Parser
;
409 if (Url
== NULL
|| Length
== 0 || UrlParser
== NULL
) {
410 return EFI_INVALID_PARAMETER
;
413 Parser
= AllocateZeroPool (sizeof (HTTP_URL_PARSER
));
414 if (Parser
== NULL
) {
415 return EFI_OUT_OF_RESOURCES
;
418 if (IsConnectMethod
) {
420 // According to RFC 2616, the authority form is only used by the CONNECT method.
422 State
= UrlParserAuthority
;
424 State
= UrlParserUrlStart
;
427 Field
= HTTP_URI_FIELD_MAX
;
430 for (Char
= Url
; Char
< Url
+ Length
; Char
++) {
432 // Update state machine accoring to next char.
434 State
= NetHttpParseUrlChar (*Char
, State
);
437 case UrlParserStateMax
:
438 return EFI_INVALID_PARAMETER
;
440 case UrlParserSchemeColon
:
441 case UrlParserSchemeColonSlash
:
442 case UrlParserSchemeColonSlashSlash
:
443 case UrlParserQueryStart
:
444 case UrlParserFragmentStart
:
446 // Skip all the delimiting char: "://" "?" "@"
450 case UrlParserScheme
:
451 Field
= HTTP_URI_FIELD_SCHEME
;
454 case UrlParserAtInAuthority
:
456 case UrlParserAuthority
:
457 Field
= HTTP_URI_FIELD_AUTHORITY
;
461 Field
= HTTP_URI_FIELD_PATH
;
465 Field
= HTTP_URI_FIELD_QUERY
;
468 case UrlParserFragment
:
469 Field
= HTTP_URI_FIELD_FRAGMENT
;
477 // Field not changed, count the length.
479 ASSERT (Field
< HTTP_URI_FIELD_MAX
);
480 if (Field
== OldField
) {
481 Parser
->FieldData
[Field
].Length
++;
488 Parser
->FieldBitMap
|= BIT (Field
);
489 Parser
->FieldData
[Field
].Offset
= (UINT32
) (Char
- Url
);
490 Parser
->FieldData
[Field
].Length
= 1;
495 // If has authority component, continue to parse the username, host and port.
497 if ((Parser
->FieldBitMap
& BIT (HTTP_URI_FIELD_AUTHORITY
)) != 0) {
498 Status
= NetHttpParseAuthority (Url
, FoundAt
, Parser
);
499 if (EFI_ERROR (Status
)) {
509 Get the Hostname from a HTTP URL.
511 This function will return the HostName according to the Url and previous parse result ,and
512 it is the caller's responsibility to free the buffer returned in *HostName.
514 @param[in] Url The pointer to a HTTP URL string.
515 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
516 @param[out] HostName Pointer to a buffer to store the HostName.
518 @retval EFI_SUCCESS Successfully get the required component.
519 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
520 @retval EFI_NOT_FOUND No hostName component in the URL.
521 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
535 HTTP_URL_PARSER
*Parser
;
537 if (Url
== NULL
|| UrlParser
== NULL
|| HostName
== NULL
) {
538 return EFI_INVALID_PARAMETER
;
541 Parser
= (HTTP_URL_PARSER
*) UrlParser
;
543 if ((Parser
->FieldBitMap
& BIT (HTTP_URI_FIELD_HOST
)) == 0) {
544 return EFI_NOT_FOUND
;
547 Name
= AllocatePool (Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Length
+ 1);
549 return EFI_OUT_OF_RESOURCES
;
552 Status
= UriPercentDecode (
553 Url
+ Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Offset
,
554 Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Length
,
558 if (EFI_ERROR (Status
)) {
562 Name
[ResultLength
] = '\0';
569 Get the IPv4 address from a HTTP URL.
571 This function will return the IPv4 address according to the Url and previous parse result.
573 @param[in] Url The pointer to a HTTP URL string.
574 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
575 @param[out] Ip4Address Pointer to a buffer to store the IP address.
577 @retval EFI_SUCCESS Successfully get the required component.
578 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
579 @retval EFI_NOT_FOUND No IPv4 address component in the URL.
580 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
588 OUT EFI_IPv4_ADDRESS
*Ip4Address
594 HTTP_URL_PARSER
*Parser
;
596 if (Url
== NULL
|| UrlParser
== NULL
|| Ip4Address
== NULL
) {
597 return EFI_INVALID_PARAMETER
;
600 Parser
= (HTTP_URL_PARSER
*) UrlParser
;
602 if ((Parser
->FieldBitMap
& BIT (HTTP_URI_FIELD_HOST
)) == 0) {
603 return EFI_INVALID_PARAMETER
;
606 Ip4String
= AllocatePool (Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Length
+ 1);
607 if (Ip4String
== NULL
) {
608 return EFI_OUT_OF_RESOURCES
;
611 Status
= UriPercentDecode (
612 Url
+ Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Offset
,
613 Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Length
,
617 if (EFI_ERROR (Status
)) {
621 Ip4String
[ResultLength
] = '\0';
622 Status
= NetLibAsciiStrToIp4 (Ip4String
, Ip4Address
);
623 FreePool (Ip4String
);
629 Get the IPv6 address from a HTTP URL.
631 This function will return the IPv6 address according to the Url and previous parse result.
633 @param[in] Url The pointer to a HTTP URL string.
634 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
635 @param[out] Ip6Address Pointer to a buffer to store the IP address.
637 @retval EFI_SUCCESS Successfully get the required component.
638 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
639 @retval EFI_NOT_FOUND No IPv6 address component in the URL.
640 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
648 OUT EFI_IPv6_ADDRESS
*Ip6Address
656 HTTP_URL_PARSER
*Parser
;
658 if (Url
== NULL
|| UrlParser
== NULL
|| Ip6Address
== NULL
) {
659 return EFI_INVALID_PARAMETER
;
662 Parser
= (HTTP_URL_PARSER
*) UrlParser
;
664 if ((Parser
->FieldBitMap
& BIT (HTTP_URI_FIELD_HOST
)) == 0) {
665 return EFI_INVALID_PARAMETER
;
669 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"
671 Length
= Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Length
;
673 return EFI_INVALID_PARAMETER
;
676 Ptr
= Url
+ Parser
->FieldData
[HTTP_URI_FIELD_HOST
].Offset
;
677 if ((Ptr
[0] != '[') || (Ptr
[Length
- 1] != ']')) {
678 return EFI_INVALID_PARAMETER
;
681 Ip6String
= AllocatePool (Length
);
682 if (Ip6String
== NULL
) {
683 return EFI_OUT_OF_RESOURCES
;
686 Status
= UriPercentDecode (
692 if (EFI_ERROR (Status
)) {
696 Ip6String
[ResultLength
] = '\0';
697 Status
= NetLibAsciiStrToIp6 (Ip6String
, Ip6Address
);
698 FreePool (Ip6String
);
704 Get the port number from a HTTP URL.
706 This function will return the port number according to the Url and previous parse result.
708 @param[in] Url The pointer to a HTTP URL string.
709 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
710 @param[out] Port Pointer to a buffer to store the port number.
712 @retval EFI_SUCCESS Successfully get the required component.
713 @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.
714 @retval EFI_NOT_FOUND No port number in the URL.
715 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
729 HTTP_URL_PARSER
*Parser
;
731 if (Url
== NULL
|| UrlParser
== NULL
|| Port
== NULL
) {
732 return EFI_INVALID_PARAMETER
;
735 Parser
= (HTTP_URL_PARSER
*) UrlParser
;
737 if ((Parser
->FieldBitMap
& BIT (HTTP_URI_FIELD_PORT
)) == 0) {
738 return EFI_INVALID_PARAMETER
;
741 PortString
= AllocatePool (Parser
->FieldData
[HTTP_URI_FIELD_PORT
].Length
+ 1);
742 if (PortString
== NULL
) {
743 return EFI_OUT_OF_RESOURCES
;
746 Status
= UriPercentDecode (
747 Url
+ Parser
->FieldData
[HTTP_URI_FIELD_PORT
].Offset
,
748 Parser
->FieldData
[HTTP_URI_FIELD_PORT
].Length
,
752 if (EFI_ERROR (Status
)) {
756 PortString
[ResultLength
] = '\0';
757 *Port
= (UINT16
) AsciiStrDecimalToUintn (Url
+ Parser
->FieldData
[HTTP_URI_FIELD_PORT
].Offset
);
763 Release the resource of the URL parser.
765 @param[in] UrlParser Pointer to the parser.
774 FreePool (UrlParser
);
778 Find a specified header field according to the field name.
780 @param[in] HeaderCount Number of HTTP header structures in Headers list.
781 @param[in] Headers Array containing list of HTTP headers.
782 @param[in] FieldName Null terminated string which describes a field name.
784 @return Pointer to the found header or NULL.
789 IN UINTN HeaderCount
,
790 IN EFI_HTTP_HEADER
*Headers
,
796 if (HeaderCount
== 0 || Headers
== NULL
|| FieldName
== NULL
) {
800 for (Index
= 0; Index
< HeaderCount
; Index
++){
802 // Field names are case-insensitive (RFC 2616).
804 if (AsciiStriCmp (Headers
[Index
].FieldName
, FieldName
) == 0) {
805 return &Headers
[Index
];
813 BodyParserBodyIdentity
,
814 BodyParserChunkSizeStart
,
816 BodyParserChunkSizeEndCR
,
817 BodyParserChunkExtStart
,
818 BodyParserChunkDataStart
,
819 BodyParserChunkDataEnd
,
820 BodyParserChunkDataEndCR
,
823 } HTTP_BODY_PARSE_STATE
;
826 BOOLEAN IgnoreBody
; // "MUST NOT" include a message-body
827 BOOLEAN IsChunked
; // "chunked" transfer-coding.
828 BOOLEAN ContentLengthIsValid
;
829 UINTN ContentLength
; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
831 HTTP_BODY_PARSER_CALLBACK Callback
;
833 UINTN ParsedBodyLength
;
834 HTTP_BODY_PARSE_STATE State
;
835 UINTN CurrentChunkSize
;
836 UINTN CurrentChunkParsedSize
;
841 Convert an Ascii char to its uppercase.
843 @param[in] Char Ascii character.
845 @return Uppercase value of the input Char.
853 if (Char
>= 'a' && Char
<= 'z') {
854 return Char
- ('a' - 'A');
861 Convert an hexadecimal char to a value of type UINTN.
863 @param[in] Char Ascii character.
865 @return Value translated from Char.
869 HttpIoHexCharToUintn (
873 if (Char
>= '0' && Char
<= '9') {
877 return (10 + HttpIoCharToUpper (Char
) - 'A');
881 Get the value of the content length if there is a "Content-Length" header.
883 @param[in] HeaderCount Number of HTTP header structures in Headers.
884 @param[in] Headers Array containing list of HTTP headers.
885 @param[out] ContentLength Pointer to save the value of the content length.
887 @retval EFI_SUCCESS Successfully get the content length.
888 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
892 HttpIoParseContentLengthHeader (
893 IN UINTN HeaderCount
,
894 IN EFI_HTTP_HEADER
*Headers
,
895 OUT UINTN
*ContentLength
898 EFI_HTTP_HEADER
*Header
;
900 Header
= HttpIoFindHeader (HeaderCount
, Headers
, "Content-Length");
901 if (Header
== NULL
) {
902 return EFI_NOT_FOUND
;
905 *ContentLength
= AsciiStrDecimalToUintn (Header
->FieldValue
);
911 Check whether the HTTP message is using the "chunked" transfer-coding.
913 @param[in] HeaderCount Number of HTTP header structures in Headers.
914 @param[in] Headers Array containing list of HTTP headers.
916 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).
921 IN UINTN HeaderCount
,
922 IN EFI_HTTP_HEADER
*Headers
925 EFI_HTTP_HEADER
*Header
;
928 Header
= HttpIoFindHeader (HeaderCount
, Headers
, "Transfer-Encoding");
929 if (Header
== NULL
) {
933 if (AsciiStriCmp (Header
->FieldValue
, "identity") != 0) {
941 Check whether the HTTP message should have a message-body.
943 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
944 @param[in] StatusCode Response status code returned by the remote host.
946 @return The message should have a message-body (FALSE) or not (TRUE).
950 HttpIoNoMessageBody (
951 IN EFI_HTTP_METHOD Method
,
952 IN EFI_HTTP_STATUS_CODE StatusCode
957 // All responses to the HEAD request method
958 // MUST NOT include a message-body, even though the presence of entity-
959 // header fields might lead one to believe they do. All 1xx
960 // (informational), 204 (no content), and 304 (not modified) responses
961 // MUST NOT include a message-body. All other responses do include a
962 // message-body, although it MAY be of zero length.
964 if (Method
== HttpMethodHead
) {
968 if ((StatusCode
== HTTP_STATUS_100_CONTINUE
) ||
969 (StatusCode
== HTTP_STATUS_101_SWITCHING_PROTOCOLS
) ||
970 (StatusCode
== HTTP_STATUS_204_NO_CONTENT
) ||
971 (StatusCode
== HTTP_STATUS_304_NOT_MODIFIED
))
980 Initialize a HTTP message-body parser.
982 This function will create and initialize a HTTP message parser according to caller provided HTTP message
983 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
985 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
986 @param[in] StatusCode Response status code returned by the remote host.
987 @param[in] HeaderCount Number of HTTP header structures in Headers.
988 @param[in] Headers Array containing list of HTTP headers.
989 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,
990 set to NULL to ignore all events.
991 @param[in] Context Pointer to the context that will be passed to Callback.
992 @param[out] MsgParser Pointer to the returned buffer to store the message parser.
994 @retval EFI_SUCCESS Successfully initialized the parser.
995 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
996 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
997 @retval Others Failed to initialize the parser.
1003 IN EFI_HTTP_METHOD Method
,
1004 IN EFI_HTTP_STATUS_CODE StatusCode
,
1005 IN UINTN HeaderCount
,
1006 IN EFI_HTTP_HEADER
*Headers
,
1007 IN HTTP_BODY_PARSER_CALLBACK Callback
,
1009 OUT VOID
**MsgParser
1013 HTTP_BODY_PARSER
*Parser
;
1015 if (HeaderCount
!= 0 && Headers
== NULL
) {
1016 return EFI_INVALID_PARAMETER
;
1019 if (MsgParser
== NULL
) {
1020 return EFI_INVALID_PARAMETER
;
1023 Parser
= AllocateZeroPool (sizeof (HTTP_BODY_PARSER
));
1024 if (Parser
== NULL
) {
1025 return EFI_OUT_OF_RESOURCES
;
1028 Parser
->State
= BodyParserBodyStart
;
1031 // Determine the message length accroding to RFC 2616.
1032 // 1. Check whether the message "MUST NOT" have a message-body.
1034 Parser
->IgnoreBody
= HttpIoNoMessageBody (Method
, StatusCode
);
1036 // 2. Check whether the message using "chunked" transfer-coding.
1038 Parser
->IsChunked
= HttpIoIsChunked (HeaderCount
, Headers
);
1040 // 3. Check whether the message has a Content-Length header field.
1042 Status
= HttpIoParseContentLengthHeader (HeaderCount
, Headers
, &Parser
->ContentLength
);
1043 if (!EFI_ERROR (Status
)) {
1044 Parser
->ContentLengthIsValid
= TRUE
;
1047 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
1048 // 5. By server closing the connection
1052 // Set state to skip body parser if the message shouldn't have a message body.
1054 if (Parser
->IgnoreBody
) {
1055 Parser
->State
= BodyParserComplete
;
1057 Parser
->Callback
= Callback
;
1058 Parser
->Context
= Context
;
1061 *MsgParser
= Parser
;
1068 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
1070 @param[in, out] MsgParser Pointer to the message parser.
1071 @param[in] BodyLength Length in bytes of the Body.
1072 @param[in] Body Pointer to the buffer of the message-body to be parsed.
1074 @retval EFI_SUCCESS Successfully parse the message-body.
1075 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.
1076 @retval Others Operation aborted.
1081 HttpParseMessageBody (
1082 IN OUT VOID
*MsgParser
,
1083 IN UINTN BodyLength
,
1088 UINTN RemainderLengthInThis
;
1089 UINTN LengthForCallback
;
1091 HTTP_BODY_PARSER
*Parser
;
1093 if (BodyLength
== 0 || Body
== NULL
) {
1094 return EFI_INVALID_PARAMETER
;
1097 if (MsgParser
== NULL
) {
1098 return EFI_INVALID_PARAMETER
;
1101 Parser
= (HTTP_BODY_PARSER
*) MsgParser
;
1103 if (Parser
->IgnoreBody
) {
1104 Parser
->State
= BodyParserComplete
;
1105 if (Parser
->Callback
!= NULL
) {
1106 Status
= Parser
->Callback (
1107 BodyParseEventOnComplete
,
1112 if (EFI_ERROR (Status
)) {
1119 if (Parser
->State
== BodyParserBodyStart
) {
1120 Parser
->ParsedBodyLength
= 0;
1121 if (Parser
->IsChunked
) {
1122 Parser
->State
= BodyParserChunkSizeStart
;
1124 Parser
->State
= BodyParserBodyIdentity
;
1129 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
1131 for (Char
= Body
; Char
< Body
+ BodyLength
; ) {
1133 switch (Parser
->State
) {
1134 case BodyParserStateMax
:
1137 case BodyParserComplete
:
1138 if (Parser
->Callback
!= NULL
) {
1139 Status
= Parser
->Callback (
1140 BodyParseEventOnComplete
,
1145 if (EFI_ERROR (Status
)) {
1151 case BodyParserBodyIdentity
:
1153 // Identity transfer-coding, just notify user to save the body data.
1155 if (Parser
->Callback
!= NULL
) {
1156 Status
= Parser
->Callback (
1157 BodyParseEventOnData
,
1159 MIN (BodyLength
, Parser
->ContentLength
- Parser
->ParsedBodyLength
),
1162 if (EFI_ERROR (Status
)) {
1166 Char
+= MIN (BodyLength
, Parser
->ContentLength
- Parser
->ParsedBodyLength
);
1167 Parser
->ParsedBodyLength
+= MIN (BodyLength
, Parser
->ContentLength
- Parser
->ParsedBodyLength
);
1168 if (Parser
->ParsedBodyLength
== Parser
->ContentLength
) {
1169 Parser
->State
= BodyParserComplete
;
1173 case BodyParserChunkSizeStart
:
1175 // First byte of chunk-size, the chunk-size might be truncated.
1177 Parser
->CurrentChunkSize
= 0;
1178 Parser
->State
= BodyParserChunkSize
;
1179 case BodyParserChunkSize
:
1180 if (!NET_IS_HEX_CHAR (*Char
)) {
1182 Parser
->State
= BodyParserChunkExtStart
;
1184 } else if (*Char
== '\r') {
1185 Parser
->State
= BodyParserChunkSizeEndCR
;
1188 Parser
->State
= BodyParserStateMax
;
1193 if (Parser
->CurrentChunkSize
> (((~((UINTN
) 0)) - 16) / 16)) {
1194 return EFI_INVALID_PARAMETER
;
1196 Parser
->CurrentChunkSize
= Parser
->CurrentChunkSize
* 16 + HttpIoHexCharToUintn (*Char
);
1200 case BodyParserChunkExtStart
:
1202 // Ignore all the chunk extensions.
1204 if (*Char
== '\r') {
1205 Parser
->State
= BodyParserChunkSizeEndCR
;
1210 case BodyParserChunkSizeEndCR
:
1211 if (*Char
!= '\n') {
1212 Parser
->State
= BodyParserStateMax
;
1215 Parser
->State
= BodyParserChunkDataStart
;
1216 Parser
->CurrentChunkParsedSize
= 0;
1220 case BodyParserChunkDataStart
:
1221 if (Parser
->CurrentChunkSize
== 0) {
1223 // This is the last chunk, the trailer header is unsupported.
1225 Parser
->ContentLengthIsValid
= TRUE
;
1226 Parser
->State
= BodyParserComplete
;
1231 // First byte of chunk-data, the chunk data also might be truncated.
1233 RemainderLengthInThis
= BodyLength
- (Char
- Body
);
1234 LengthForCallback
= MIN (Parser
->CurrentChunkSize
- Parser
->CurrentChunkParsedSize
, RemainderLengthInThis
);
1235 if (Parser
->Callback
!= NULL
) {
1236 Status
= Parser
->Callback (
1237 BodyParseEventOnData
,
1242 if (EFI_ERROR (Status
)) {
1246 Char
+= LengthForCallback
;
1247 Parser
->ContentLength
+= LengthForCallback
;
1248 Parser
->CurrentChunkParsedSize
+= LengthForCallback
;
1249 if (Parser
->CurrentChunkParsedSize
== Parser
->CurrentChunkSize
) {
1250 Parser
->State
= BodyParserChunkDataEnd
;
1254 case BodyParserChunkDataEnd
:
1255 if (*Char
== '\r') {
1256 Parser
->State
= BodyParserChunkDataEndCR
;
1258 Parser
->State
= BodyParserStateMax
;
1263 case BodyParserChunkDataEndCR
:
1264 if (*Char
!= '\n') {
1265 Parser
->State
= BodyParserStateMax
;
1269 Parser
->State
= BodyParserChunkSizeStart
;
1278 if (Parser
->State
== BodyParserStateMax
) {
1286 Check whether the message-body is complete or not.
1288 @param[in] MsgParser Pointer to the message parser.
1290 @retval TRUE Message-body is complete.
1291 @retval FALSE Message-body is not complete.
1296 HttpIsMessageComplete (
1300 HTTP_BODY_PARSER
*Parser
;
1302 Parser
= (HTTP_BODY_PARSER
*) MsgParser
;
1304 if (Parser
->State
== BodyParserComplete
) {
1311 Get the content length of the entity.
1313 Note that in trunk transfer, the entity length is not valid until the whole message body is received.
1315 @param[in] MsgParser Pointer to the message parser.
1316 @param[out] ContentLength Pointer to store the length of the entity.
1318 @retval EFI_SUCCESS Successfully to get the entity length.
1319 @retval EFI_NOT_READY Entity length is not valid yet.
1320 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.
1325 HttpGetEntityLength (
1327 OUT UINTN
*ContentLength
1330 HTTP_BODY_PARSER
*Parser
;
1332 if (MsgParser
== NULL
|| ContentLength
== NULL
) {
1333 return EFI_INVALID_PARAMETER
;
1336 Parser
= (HTTP_BODY_PARSER
*) MsgParser
;
1338 if (!Parser
->ContentLengthIsValid
) {
1339 return EFI_NOT_READY
;
1342 *ContentLength
= Parser
->ContentLength
;
1347 Release the resource of the message parser.
1349 @param[in] MsgParser Pointer to the message parser.
1358 FreePool (MsgParser
);