]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c
NetworkPkg:Enable Http Boot over Ipv6 stack
[mirror_edk2.git] / MdeModulePkg / Library / DxeHttpLib / DxeHttpLib.c
CommitLineData
f9a14916
JW
1/** @file\r
2 This library is used to share code between UEFI network stack modules.\r
3 It provides the helper routines to parse the HTTP message byte stream.\r
4\r
5Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at<BR>\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include <Uefi.h>\r
17#include <Library/NetLib.h>\r
18#include <Library/HttpLib.h>\r
19#include <Library/BaseLib.h>\r
20#include <Library/DebugLib.h>\r
21#include <Library/MemoryAllocationLib.h>\r
22#include <Library/UefiBootServicesTableLib.h>\r
23\r
24#define BIT(x) (1 << x)\r
25\r
26#define NET_IS_HEX_CHAR(Ch) \\r
27 ((('0' <= (Ch)) && ((Ch) <= '9')) || \\r
28 (('A' <= (Ch)) && ((Ch) <= 'F')) || \\r
29 (('a' <= (Ch)) && ((Ch) <= 'f')))\r
30\r
31//\r
32// Field index of the HTTP URL parse result.\r
33//\r
34#define HTTP_URI_FIELD_SCHEME 0\r
35#define HTTP_URI_FIELD_AUTHORITY 1\r
36#define HTTP_URI_FIELD_PATH 2\r
37#define HTTP_URI_FIELD_QUERY 3\r
38#define HTTP_URI_FIELD_FRAGMENT 4\r
39#define HTTP_URI_FIELD_USERINFO 5\r
40#define HTTP_URI_FIELD_HOST 6\r
41#define HTTP_URI_FIELD_PORT 7\r
42#define HTTP_URI_FIELD_MAX 8\r
43\r
44//\r
45// Structure to store the parse result of a HTTP URL.\r
46//\r
47typedef struct {\r
48 UINT32 Offset;\r
49 UINT32 Length;\r
50} HTTP_URL_FILED_DATA;\r
51\r
52typedef struct {\r
53 UINT16 FieldBitMap;\r
54 HTTP_URL_FILED_DATA FieldData[HTTP_URI_FIELD_MAX];\r
55} HTTP_URL_PARSER;\r
56\r
57typedef enum {\r
58 UrlParserUrlStart,\r
59 UrlParserScheme,\r
60 UrlParserSchemeColon, // ":"\r
61 UrlParserSchemeColonSlash, // ":/"\r
62 UrlParserSchemeColonSlashSlash, // "://"\r
63 UrlParserAuthority,\r
64 UrlParserAtInAuthority,\r
65 UrlParserPath,\r
66 UrlParserQueryStart, // "?"\r
67 UrlParserQuery,\r
68 UrlParserFragmentStart, // "#"\r
69 UrlParserFragment,\r
70 UrlParserUserInfo,\r
71 UrlParserHostStart, // "@"\r
72 UrlParserHost,\r
73 UrlParserPortStart, // ":"\r
74 UrlParserPort,\r
75 UrlParserStateMax\r
76} HTTP_URL_PARSE_STATE;\r
77\r
78/**\r
79 Decode a percent-encoded URI component to the ASCII character.\r
80 \r
81 Decode the input component in Buffer according to RFC 3986. The caller is responsible to make \r
82 sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))\r
83 in bytes. \r
84\r
85 @param[in] Buffer The pointer to a percent-encoded URI component.\r
86 @param[in] BufferLength Length of Buffer in bytes.\r
87 @param[out] ResultBuffer Point to the buffer to store the decode result.\r
88 @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.\r
89\r
90 @retval EFI_SUCCESS Successfully decoded the URI.\r
91 @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.\r
92 \r
93**/\r
94EFI_STATUS\r
95EFIAPI\r
96UriPercentDecode (\r
97 IN CHAR8 *Buffer,\r
98 IN UINT32 BufferLength,\r
99 OUT CHAR8 *ResultBuffer,\r
100 OUT UINT32 *ResultLength\r
101 )\r
102{\r
103 UINTN Index;\r
104 UINTN Offset;\r
105 CHAR8 HexStr[3];\r
106\r
107 if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {\r
108 return EFI_INVALID_PARAMETER;\r
109 }\r
110 \r
111 Index = 0;\r
112 Offset = 0;\r
113 HexStr[2] = '\0';\r
114 while (Index < BufferLength) {\r
115 if (Buffer[Index] == '%') {\r
116 if (!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {\r
117 return EFI_INVALID_PARAMETER;\r
118 }\r
119 HexStr[0] = Buffer[Index+1];\r
120 HexStr[1] = Buffer[Index+2];\r
121 ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);\r
122 Index += 3;\r
123 } else {\r
124 ResultBuffer[Offset] = Buffer[Index];\r
125 Index++;\r
126 }\r
127 Offset++;\r
128 }\r
129\r
130 *ResultLength = (UINT32) Offset;\r
131 \r
132 return EFI_SUCCESS;\r
133}\r
134\r
135/**\r
136 This function return the updated state accroding to the input state and next character of\r
137 the authority.\r
138\r
139 @param[in] Char Next character.\r
140 @param[in] State Current value of the parser state machine.\r
141\r
142 @return Updated state value.\r
143**/\r
144HTTP_URL_PARSE_STATE\r
145NetHttpParseAuthorityChar (\r
146 IN CHAR8 Char,\r
147 IN HTTP_URL_PARSE_STATE State\r
148 )\r
149{\r
150\r
151 //\r
152 // RFC 3986:\r
153 // The authority component is preceded by a double slash ("//") and is\r
154 // terminated by the next slash ("/"), question mark ("?"), or number\r
155 // sign ("#") character, or by the end of the URI.\r
156 //\r
157 if (Char == ' ' || Char == '\r' || Char == '\n') {\r
158 return UrlParserStateMax;\r
159 }\r
160\r
161 //\r
162 // authority = [ userinfo "@" ] host [ ":" port ]\r
163 //\r
164 switch (State) {\r
165 case UrlParserUserInfo:\r
166 if (Char == '@') {\r
167 return UrlParserHostStart;\r
168 }\r
169 break;\r
170\r
171 case UrlParserHost:\r
172 case UrlParserHostStart:\r
173 if (Char == ':') {\r
174 return UrlParserPortStart;\r
175 }\r
176 return UrlParserHost;\r
177\r
178 case UrlParserPort:\r
179 case UrlParserPortStart:\r
180 return UrlParserPort;\r
181\r
182 default:\r
183 break;\r
184 }\r
185\r
186 return State;\r
187}\r
188\r
189/**\r
190 This function parse the authority component of the input URL and update the parser.\r
191\r
192 @param[in] Url The pointer to a HTTP URL string.\r
193 @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.\r
194 @param[in, out] UrlParser Pointer to the buffer of the parse result.\r
195\r
196 @retval EFI_SUCCESS Successfully parse the authority.\r
197 @retval Other Error happened.\r
198\r
199**/\r
200EFI_STATUS\r
201NetHttpParseAuthority (\r
202 IN CHAR8 *Url,\r
203 IN BOOLEAN FoundAt,\r
204 IN OUT HTTP_URL_PARSER *UrlParser\r
205 )\r
206{\r
207 CHAR8 *Char;\r
208 CHAR8 *Authority;\r
209 UINT32 Length;\r
210 HTTP_URL_PARSE_STATE State;\r
211 UINT32 Field;\r
212 UINT32 OldField;\r
213 \r
214 ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);\r
215\r
216 //\r
217 // authority = [ userinfo "@" ] host [ ":" port ]\r
218 //\r
219 if (FoundAt) {\r
220 State = UrlParserUserInfo;\r
221 } else {\r
222 State = UrlParserHost;\r
223 }\r
224\r
225 Field = HTTP_URI_FIELD_MAX;\r
226 OldField = Field;\r
227 Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;\r
228 Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;\r
229 for (Char = Authority; Char < Authority + Length; Char++) {\r
230 State = NetHttpParseAuthorityChar (*Char, State);\r
231 switch (State) {\r
232 case UrlParserStateMax:\r
233 return EFI_INVALID_PARAMETER;\r
234\r
235 case UrlParserHostStart:\r
236 case UrlParserPortStart:\r
237 continue;\r
238\r
239 case UrlParserUserInfo:\r
240 Field = HTTP_URI_FIELD_USERINFO;\r
241 break;\r
242 \r
243 case UrlParserHost:\r
244 Field = HTTP_URI_FIELD_HOST;\r
245 break;\r
246 \r
247 case UrlParserPort:\r
248 Field = HTTP_URI_FIELD_PORT;\r
249 break;\r
250\r
251 default:\r
252 ASSERT (FALSE);\r
253 }\r
254\r
255 //\r
256 // Field not changed, count the length.\r
257 //\r
258 ASSERT (Field < HTTP_URI_FIELD_MAX);\r
259 if (Field == OldField) {\r
260 UrlParser->FieldData[Field].Length++;\r
261 continue;\r
262 }\r
263\r
264 //\r
265 // New field start\r
266 //\r
267 UrlParser->FieldBitMap |= BIT (Field);\r
268 UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);\r
269 UrlParser->FieldData[Field].Length = 1;\r
270 OldField = Field;\r
271 }\r
272\r
273 return EFI_SUCCESS;\r
274}\r
275\r
276/**\r
277 This function return the updated state accroding to the input state and next character of a URL.\r
278\r
279 @param[in] Char Next character.\r
280 @param[in] State Current value of the parser state machine.\r
281\r
282 @return Updated state value.\r
283\r
284**/\r
285HTTP_URL_PARSE_STATE\r
286NetHttpParseUrlChar (\r
287 IN CHAR8 Char,\r
288 IN HTTP_URL_PARSE_STATE State\r
289 )\r
290{\r
291 if (Char == ' ' || Char == '\r' || Char == '\n') {\r
292 return UrlParserStateMax;\r
293 }\r
294 \r
295 //\r
296 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]\r
297 // \r
298 // Request-URI = "*" | absolute-URI | path-absolute | authority\r
299 // \r
300 // absolute-URI = scheme ":" hier-part [ "?" query ]\r
301 // path-absolute = "/" [ segment-nz *( "/" segment ) ]\r
302 // authority = [ userinfo "@" ] host [ ":" port ]\r
303 //\r
304 switch (State) {\r
305 case UrlParserUrlStart:\r
306 if (Char == '*' || Char == '/') {\r
307 return UrlParserPath;\r
308 }\r
309 return UrlParserScheme;\r
310\r
311 case UrlParserScheme:\r
312 if (Char == ':') {\r
313 return UrlParserSchemeColon;\r
314 }\r
315 break;\r
316\r
317 case UrlParserSchemeColon:\r
318 if (Char == '/') {\r
319 return UrlParserSchemeColonSlash;\r
320 }\r
321 break;\r
322\r
323 case UrlParserSchemeColonSlash:\r
324 if (Char == '/') {\r
325 return UrlParserSchemeColonSlashSlash;\r
326 }\r
327 break;\r
328\r
329 case UrlParserAtInAuthority:\r
330 if (Char == '@') {\r
331 return UrlParserStateMax;\r
332 }\r
333\r
334 case UrlParserAuthority:\r
335 case UrlParserSchemeColonSlashSlash:\r
336 if (Char == '@') {\r
337 return UrlParserAtInAuthority;\r
338 }\r
339 if (Char == '/') {\r
340 return UrlParserPath;\r
341 }\r
342 if (Char == '?') {\r
343 return UrlParserQueryStart;\r
344 }\r
345 if (Char == '#') {\r
346 return UrlParserFragmentStart;\r
347 }\r
348 return UrlParserAuthority;\r
349\r
350 case UrlParserPath:\r
351 if (Char == '?') {\r
352 return UrlParserQueryStart;\r
353 }\r
354 if (Char == '#') {\r
355 return UrlParserFragmentStart;\r
356 }\r
357 break;\r
358\r
359 case UrlParserQuery:\r
360 case UrlParserQueryStart:\r
361 if (Char == '#') {\r
362 return UrlParserFragmentStart;\r
363 }\r
364 return UrlParserQuery;\r
365\r
366 case UrlParserFragmentStart:\r
367 return UrlParserFragment;\r
368 \r
369 default:\r
370 break;\r
371 }\r
372\r
373 return State;\r
374}\r
375/**\r
376 Create a URL parser for the input URL string.\r
377\r
378 This function will parse and dereference the input HTTP URL into it components. The original\r
379 content of the URL won't be modified and the result will be returned in UrlParser, which can\r
380 be used in other functions like NetHttpUrlGetHostName().\r
381\r
382 @param[in] Url The pointer to a HTTP URL string.\r
383 @param[in] Length Length of Url in bytes.\r
384 @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.\r
385 @param[out] UrlParser Pointer to the returned buffer to store the parse result.\r
386\r
387 @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.\r
388 @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.\r
389 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
390\r
391**/\r
392EFI_STATUS\r
393EFIAPI\r
394HttpParseUrl (\r
395 IN CHAR8 *Url,\r
396 IN UINT32 Length,\r
397 IN BOOLEAN IsConnectMethod,\r
398 OUT VOID **UrlParser\r
399 )\r
400{\r
401 HTTP_URL_PARSE_STATE State;\r
402 CHAR8 *Char;\r
403 UINT32 Field;\r
404 UINT32 OldField;\r
405 BOOLEAN FoundAt;\r
406 EFI_STATUS Status;\r
407 HTTP_URL_PARSER *Parser;\r
408 \r
409 if (Url == NULL || Length == 0 || UrlParser == NULL) {\r
410 return EFI_INVALID_PARAMETER;\r
411 }\r
412\r
413 Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));\r
414 if (Parser == NULL) {\r
415 return EFI_OUT_OF_RESOURCES;\r
416 }\r
417 \r
418 if (IsConnectMethod) {\r
419 //\r
420 // According to RFC 2616, the authority form is only used by the CONNECT method.\r
421 //\r
422 State = UrlParserAuthority;\r
423 } else {\r
424 State = UrlParserUrlStart;\r
425 }\r
426\r
427 Field = HTTP_URI_FIELD_MAX;\r
428 OldField = Field;\r
429 FoundAt = FALSE;\r
430 for (Char = Url; Char < Url + Length; Char++) {\r
431 //\r
432 // Update state machine accoring to next char.\r
433 //\r
434 State = NetHttpParseUrlChar (*Char, State);\r
435\r
436 switch (State) {\r
437 case UrlParserStateMax:\r
438 return EFI_INVALID_PARAMETER;\r
439 \r
440 case UrlParserSchemeColon:\r
441 case UrlParserSchemeColonSlash:\r
442 case UrlParserSchemeColonSlashSlash:\r
443 case UrlParserQueryStart:\r
444 case UrlParserFragmentStart:\r
445 //\r
446 // Skip all the delimiting char: "://" "?" "@"\r
447 //\r
448 continue;\r
449 \r
450 case UrlParserScheme:\r
451 Field = HTTP_URI_FIELD_SCHEME;\r
452 break;\r
453\r
454 case UrlParserAtInAuthority:\r
455 FoundAt = TRUE;\r
456 case UrlParserAuthority:\r
457 Field = HTTP_URI_FIELD_AUTHORITY;\r
458 break;\r
459\r
460 case UrlParserPath:\r
461 Field = HTTP_URI_FIELD_PATH;\r
462 break;\r
463\r
464 case UrlParserQuery:\r
465 Field = HTTP_URI_FIELD_QUERY;\r
466 break;\r
467\r
468 case UrlParserFragment:\r
469 Field = HTTP_URI_FIELD_FRAGMENT;\r
470 break;\r
471\r
472 default:\r
473 ASSERT (FALSE);\r
474 }\r
475\r
476 //\r
477 // Field not changed, count the length.\r
478 //\r
479 ASSERT (Field < HTTP_URI_FIELD_MAX);\r
480 if (Field == OldField) {\r
481 Parser->FieldData[Field].Length++;\r
482 continue;\r
483 }\r
484\r
485 //\r
486 // New field start\r
487 //\r
488 Parser->FieldBitMap |= BIT (Field);\r
489 Parser->FieldData[Field].Offset = (UINT32) (Char - Url);\r
490 Parser->FieldData[Field].Length = 1;\r
491 OldField = Field;\r
492 }\r
493\r
494 //\r
495 // If has authority component, continue to parse the username, host and port.\r
496 //\r
497 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {\r
498 Status = NetHttpParseAuthority (Url, FoundAt, Parser);\r
499 if (EFI_ERROR (Status)) {\r
500 return Status;\r
501 }\r
502 }\r
503\r
504 *UrlParser = Parser;\r
505 return EFI_SUCCESS; \r
506}\r
507\r
508/**\r
509 Get the Hostname from a HTTP URL.\r
510\r
511 This function will return the HostName according to the Url and previous parse result ,and\r
512 it is the caller's responsibility to free the buffer returned in *HostName.\r
513\r
514 @param[in] Url The pointer to a HTTP URL string.\r
515 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
516 @param[out] HostName Pointer to a buffer to store the HostName.\r
517\r
518 @retval EFI_SUCCESS Successfully get the required component.\r
519 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.\r
520 @retval EFI_NOT_FOUND No hostName component in the URL.\r
521 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
522 \r
523**/\r
524EFI_STATUS\r
525EFIAPI\r
526HttpUrlGetHostName (\r
527 IN CHAR8 *Url,\r
528 IN VOID *UrlParser,\r
529 OUT CHAR8 **HostName\r
530 )\r
531{\r
532 CHAR8 *Name;\r
533 EFI_STATUS Status;\r
534 UINT32 ResultLength;\r
535 HTTP_URL_PARSER *Parser;\r
536\r
537 if (Url == NULL || UrlParser == NULL || HostName == NULL) {\r
538 return EFI_INVALID_PARAMETER;\r
539 }\r
540\r
541 Parser = (HTTP_URL_PARSER*) UrlParser;\r
542\r
543 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
544 return EFI_NOT_FOUND;\r
545 }\r
546\r
547 Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
548 if (Name == NULL) {\r
549 return EFI_OUT_OF_RESOURCES;\r
550 }\r
551 \r
552 Status = UriPercentDecode (\r
553 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
554 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
555 Name,\r
556 &ResultLength\r
557 );\r
558 if (EFI_ERROR (Status)) {\r
559 return Status;\r
560 }\r
561\r
562 Name[ResultLength] = '\0';\r
563 *HostName = Name;\r
564 return EFI_SUCCESS;\r
565}\r
566\r
567\r
568/**\r
569 Get the IPv4 address from a HTTP URL.\r
570\r
571 This function will return the IPv4 address according to the Url and previous parse result.\r
572\r
573 @param[in] Url The pointer to a HTTP URL string.\r
574 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
575 @param[out] Ip4Address Pointer to a buffer to store the IP address.\r
576\r
577 @retval EFI_SUCCESS Successfully get the required component.\r
578 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.\r
579 @retval EFI_NOT_FOUND No IPv4 address component in the URL.\r
580 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
581 \r
582**/\r
583EFI_STATUS\r
584EFIAPI\r
585HttpUrlGetIp4 (\r
586 IN CHAR8 *Url,\r
587 IN VOID *UrlParser,\r
588 OUT EFI_IPv4_ADDRESS *Ip4Address\r
589 )\r
590{\r
591 CHAR8 *Ip4String;\r
592 EFI_STATUS Status;\r
593 UINT32 ResultLength;\r
594 HTTP_URL_PARSER *Parser;\r
595 \r
596 if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {\r
597 return EFI_INVALID_PARAMETER;\r
598 }\r
599\r
600 Parser = (HTTP_URL_PARSER*) UrlParser;\r
601\r
602 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
603 return EFI_INVALID_PARAMETER;\r
604 }\r
605\r
606 Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
607 if (Ip4String == NULL) {\r
608 return EFI_OUT_OF_RESOURCES;\r
609 }\r
610 \r
611 Status = UriPercentDecode (\r
612 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
613 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
614 Ip4String,\r
615 &ResultLength\r
616 );\r
617 if (EFI_ERROR (Status)) {\r
618 return Status;\r
619 }\r
620\r
621 Ip4String[ResultLength] = '\0';\r
622 Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);\r
623 FreePool (Ip4String);\r
624\r
625 return Status;\r
626}\r
627\r
628/**\r
629 Get the IPv6 address from a HTTP URL.\r
630\r
631 This function will return the IPv6 address according to the Url and previous parse result.\r
632\r
633 @param[in] Url The pointer to a HTTP URL string.\r
634 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
635 @param[out] Ip6Address Pointer to a buffer to store the IP address.\r
636\r
637 @retval EFI_SUCCESS Successfully get the required component.\r
638 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.\r
639 @retval EFI_NOT_FOUND No IPv6 address component in the URL.\r
640 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
641 \r
642**/\r
643EFI_STATUS\r
644EFIAPI\r
645HttpUrlGetIp6 (\r
646 IN CHAR8 *Url,\r
647 IN VOID *UrlParser,\r
648 OUT EFI_IPv6_ADDRESS *Ip6Address\r
649 )\r
650{\r
651 CHAR8 *Ip6String;\r
652 CHAR8 *Ptr;\r
653 UINT32 Length;\r
654 EFI_STATUS Status;\r
655 UINT32 ResultLength;\r
656 HTTP_URL_PARSER *Parser;\r
657 \r
658 if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {\r
659 return EFI_INVALID_PARAMETER;\r
660 }\r
661\r
662 Parser = (HTTP_URL_PARSER*) UrlParser;\r
663\r
664 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
665 return EFI_INVALID_PARAMETER;\r
666 }\r
667\r
668 //\r
669 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"\r
670 //\r
671 Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;\r
672 if (Length < 2) {\r
673 return EFI_INVALID_PARAMETER;\r
674 }\r
675\r
676 Ptr = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;\r
677 if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {\r
678 return EFI_INVALID_PARAMETER;\r
679 }\r
680\r
681 Ip6String = AllocatePool (Length);\r
682 if (Ip6String == NULL) {\r
683 return EFI_OUT_OF_RESOURCES;\r
684 }\r
685 \r
686 Status = UriPercentDecode (\r
687 Ptr + 1,\r
688 Length - 2,\r
689 Ip6String,\r
690 &ResultLength\r
691 );\r
692 if (EFI_ERROR (Status)) {\r
693 return Status;\r
694 }\r
695 \r
696 Ip6String[ResultLength] = '\0';\r
697 Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);\r
698 FreePool (Ip6String);\r
699\r
700 return Status;\r
701}\r
702\r
703/**\r
704 Get the port number from a HTTP URL.\r
705\r
706 This function will return the port number according to the Url and previous parse result.\r
707\r
708 @param[in] Url The pointer to a HTTP URL string.\r
709 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
710 @param[out] Port Pointer to a buffer to store the port number.\r
711\r
712 @retval EFI_SUCCESS Successfully get the required component.\r
713 @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.\r
714 @retval EFI_NOT_FOUND No port number in the URL.\r
715 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
716 \r
717**/\r
718EFI_STATUS\r
719EFIAPI\r
720HttpUrlGetPort (\r
721 IN CHAR8 *Url,\r
722 IN VOID *UrlParser,\r
723 OUT UINT16 *Port\r
724 )\r
725{\r
726 CHAR8 *PortString;\r
727 EFI_STATUS Status;\r
728 UINT32 ResultLength;\r
729 HTTP_URL_PARSER *Parser;\r
730\r
731 if (Url == NULL || UrlParser == NULL || Port == NULL) {\r
732 return EFI_INVALID_PARAMETER;\r
733 }\r
734\r
735 Parser = (HTTP_URL_PARSER*) UrlParser;\r
736\r
737 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {\r
738 return EFI_INVALID_PARAMETER;\r
739 }\r
740\r
741 PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);\r
742 if (PortString == NULL) {\r
743 return EFI_OUT_OF_RESOURCES;\r
744 }\r
745\r
746 Status = UriPercentDecode (\r
747 Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,\r
748 Parser->FieldData[HTTP_URI_FIELD_PORT].Length,\r
749 PortString,\r
750 &ResultLength\r
751 );\r
752 if (EFI_ERROR (Status)) {\r
753 return Status;\r
754 }\r
755\r
756 PortString[ResultLength] = '\0';\r
757 *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset);\r
758\r
759 return EFI_SUCCESS;\r
760}\r
761\r
762/**\r
763 Release the resource of the URL parser.\r
764\r
765 @param[in] UrlParser Pointer to the parser.\r
766 \r
767**/\r
768VOID\r
769EFIAPI\r
770HttpUrlFreeParser (\r
771 IN VOID *UrlParser\r
772 )\r
773{\r
774 FreePool (UrlParser);\r
775}\r
776\r
777/**\r
778 Find a specified header field according to the field name.\r
779\r
780 @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
781 @param[in] Headers Array containing list of HTTP headers.\r
782 @param[in] FieldName Null terminated string which describes a field name. \r
783\r
784 @return Pointer to the found header or NULL.\r
785\r
786**/\r
787EFI_HTTP_HEADER *\r
788HttpIoFindHeader (\r
789 IN UINTN HeaderCount,\r
790 IN EFI_HTTP_HEADER *Headers,\r
791 IN CHAR8 *FieldName\r
792 )\r
793{\r
794 UINTN Index;\r
795 \r
796 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {\r
797 return NULL;\r
798 }\r
799\r
800 for (Index = 0; Index < HeaderCount; Index++){\r
801 //\r
802 // Field names are case-insensitive (RFC 2616).\r
803 //\r
804 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {\r
805 return &Headers[Index];\r
806 }\r
807 }\r
808 return NULL;\r
809}\r
810\r
811typedef enum {\r
812 BodyParserBodyStart,\r
813 BodyParserBodyIdentity,\r
814 BodyParserChunkSizeStart,\r
815 BodyParserChunkSize,\r
816 BodyParserChunkSizeEndCR,\r
817 BodyParserChunkExtStart,\r
818 BodyParserChunkDataStart,\r
819 BodyParserChunkDataEnd,\r
820 BodyParserChunkDataEndCR,\r
821 BodyParserTrailer,\r
822 BodyParserLastCRLF,\r
823 BodyParserLastCRLFEnd,\r
824 BodyParserComplete,\r
825 BodyParserStateMax\r
826} HTTP_BODY_PARSE_STATE;\r
827\r
828typedef struct {\r
829 BOOLEAN IgnoreBody; // "MUST NOT" include a message-body\r
830 BOOLEAN IsChunked; // "chunked" transfer-coding.\r
831 BOOLEAN ContentLengthIsValid;\r
832 UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE\r
833 \r
834 HTTP_BODY_PARSER_CALLBACK Callback;\r
835 VOID *Context;\r
836 UINTN ParsedBodyLength;\r
837 HTTP_BODY_PARSE_STATE State;\r
838 UINTN CurrentChunkSize;\r
839 UINTN CurrentChunkParsedSize;\r
840} HTTP_BODY_PARSER;\r
841\r
842/**\r
843\r
844 Convert an Ascii char to its uppercase.\r
845\r
846 @param[in] Char Ascii character.\r
847\r
848 @return Uppercase value of the input Char.\r
849\r
850**/\r
851CHAR8\r
852HttpIoCharToUpper (\r
853 IN CHAR8 Char\r
854 )\r
855{\r
856 if (Char >= 'a' && Char <= 'z') {\r
857 return Char - ('a' - 'A');\r
858 }\r
859\r
860 return Char;\r
861}\r
862\r
863/**\r
864 Convert an hexadecimal char to a value of type UINTN.\r
865\r
866 @param[in] Char Ascii character.\r
867\r
868 @return Value translated from Char.\r
869\r
870**/\r
871UINTN\r
872HttpIoHexCharToUintn (\r
873 IN CHAR8 Char\r
874 )\r
875{\r
876 if (Char >= '0' && Char <= '9') {\r
877 return Char - '0';\r
878 }\r
879\r
880 return (10 + HttpIoCharToUpper (Char) - 'A');\r
881}\r
882\r
883/**\r
884 Get the value of the content length if there is a "Content-Length" header.\r
885\r
886 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
887 @param[in] Headers Array containing list of HTTP headers.\r
888 @param[out] ContentLength Pointer to save the value of the content length.\r
889\r
890 @retval EFI_SUCCESS Successfully get the content length.\r
891 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.\r
892\r
893**/\r
894EFI_STATUS\r
895HttpIoParseContentLengthHeader (\r
896 IN UINTN HeaderCount,\r
897 IN EFI_HTTP_HEADER *Headers,\r
898 OUT UINTN *ContentLength\r
899 )\r
900{\r
901 EFI_HTTP_HEADER *Header;\r
902 \r
903 Header = HttpIoFindHeader (HeaderCount, Headers, "Content-Length");\r
904 if (Header == NULL) {\r
905 return EFI_NOT_FOUND;\r
906 }\r
907\r
908 *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue);\r
909 return EFI_SUCCESS;\r
910}\r
911\r
912/**\r
913\r
914 Check whether the HTTP message is using the "chunked" transfer-coding.\r
915\r
916 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
917 @param[in] Headers Array containing list of HTTP headers.\r
918\r
919 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).\r
920 \r
921**/\r
922BOOLEAN\r
923HttpIoIsChunked (\r
924 IN UINTN HeaderCount,\r
925 IN EFI_HTTP_HEADER *Headers\r
926 )\r
927{\r
928 EFI_HTTP_HEADER *Header;\r
929\r
930\r
931 Header = HttpIoFindHeader (HeaderCount, Headers, "Transfer-Encoding");\r
932 if (Header == NULL) {\r
933 return FALSE;\r
934 }\r
935\r
936 if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {\r
937 return TRUE;\r
938 }\r
939\r
940 return FALSE;\r
941}\r
942\r
943/**\r
944 Check whether the HTTP message should have a message-body.\r
945\r
946 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.\r
947 @param[in] StatusCode Response status code returned by the remote host.\r
948\r
949 @return The message should have a message-body (FALSE) or not (TRUE).\r
950\r
951**/\r
952BOOLEAN\r
953HttpIoNoMessageBody (\r
954 IN EFI_HTTP_METHOD Method,\r
955 IN EFI_HTTP_STATUS_CODE StatusCode\r
956 )\r
957{\r
958 //\r
959 // RFC 2616:\r
960 // All responses to the HEAD request method\r
961 // MUST NOT include a message-body, even though the presence of entity-\r
962 // header fields might lead one to believe they do. All 1xx\r
963 // (informational), 204 (no content), and 304 (not modified) responses\r
964 // MUST NOT include a message-body. All other responses do include a\r
965 // message-body, although it MAY be of zero length.\r
966 //\r
967 if (Method == HttpMethodHead) {\r
968 return TRUE;\r
969 }\r
970\r
971 if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||\r
972 (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||\r
973 (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||\r
974 (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))\r
975 {\r
976 return TRUE;\r
977 }\r
978\r
979 return FALSE;\r
980}\r
981\r
982/**\r
983 Initialize a HTTP message-body parser.\r
984\r
985 This function will create and initialize a HTTP message parser according to caller provided HTTP message\r
986 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().\r
987\r
988 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.\r
989 @param[in] StatusCode Response status code returned by the remote host.\r
990 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
991 @param[in] Headers Array containing list of HTTP headers.\r
992 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,\r
993 set to NULL to ignore all events.\r
994 @param[in] Context Pointer to the context that will be passed to Callback.\r
995 @param[out] MsgParser Pointer to the returned buffer to store the message parser.\r
996\r
997 @retval EFI_SUCCESS Successfully initialized the parser.\r
998 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
999 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.\r
1000 @retval Others Failed to initialize the parser.\r
1001\r
1002**/\r
1003EFI_STATUS\r
1004EFIAPI\r
1005HttpInitMsgParser (\r
1006 IN EFI_HTTP_METHOD Method,\r
1007 IN EFI_HTTP_STATUS_CODE StatusCode,\r
1008 IN UINTN HeaderCount,\r
1009 IN EFI_HTTP_HEADER *Headers,\r
1010 IN HTTP_BODY_PARSER_CALLBACK Callback,\r
1011 IN VOID *Context,\r
1012 OUT VOID **MsgParser\r
1013 )\r
1014{\r
1015 EFI_STATUS Status;\r
1016 HTTP_BODY_PARSER *Parser;\r
1017 \r
1018 if (HeaderCount != 0 && Headers == NULL) {\r
1019 return EFI_INVALID_PARAMETER;\r
1020 }\r
1021\r
1022 if (MsgParser == NULL) {\r
1023 return EFI_INVALID_PARAMETER;\r
1024 }\r
1025\r
1026 Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));\r
1027 if (Parser == NULL) {\r
1028 return EFI_OUT_OF_RESOURCES;\r
1029 }\r
1030\r
1031 Parser->State = BodyParserBodyStart;\r
1032 \r
1033 //\r
1034 // Determine the message length accroding to RFC 2616.\r
1035 // 1. Check whether the message "MUST NOT" have a message-body.\r
1036 //\r
1037 Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);\r
1038 //\r
1039 // 2. Check whether the message using "chunked" transfer-coding.\r
1040 //\r
1041 Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);\r
1042 //\r
1043 // 3. Check whether the message has a Content-Length header field.\r
1044 //\r
1045 Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);\r
1046 if (!EFI_ERROR (Status)) {\r
1047 Parser->ContentLengthIsValid = TRUE;\r
1048 }\r
1049 //\r
1050 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".\r
1051 // 5. By server closing the connection\r
1052 //\r
1053 \r
1054 //\r
1055 // Set state to skip body parser if the message shouldn't have a message body.\r
1056 //\r
1057 if (Parser->IgnoreBody) {\r
1058 Parser->State = BodyParserComplete;\r
1059 } else {\r
1060 Parser->Callback = Callback;\r
1061 Parser->Context = Context;\r
1062 }\r
1063\r
1064 *MsgParser = Parser;\r
1065 return EFI_SUCCESS;\r
1066}\r
1067\r
1068/**\r
1069 Parse message body.\r
1070\r
1071 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.\r
1072\r
1073 @param[in, out] MsgParser Pointer to the message parser.\r
1074 @param[in] BodyLength Length in bytes of the Body.\r
1075 @param[in] Body Pointer to the buffer of the message-body to be parsed.\r
1076\r
1077 @retval EFI_SUCCESS Successfully parse the message-body.\r
1078 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.\r
1079 @retval Others Operation aborted.\r
1080\r
1081**/\r
1082EFI_STATUS\r
1083EFIAPI\r
1084HttpParseMessageBody (\r
1085 IN OUT VOID *MsgParser,\r
1086 IN UINTN BodyLength,\r
1087 IN CHAR8 *Body\r
1088 )\r
1089{\r
1090 CHAR8 *Char;\r
1091 UINTN RemainderLengthInThis;\r
1092 UINTN LengthForCallback;\r
1093 EFI_STATUS Status;\r
1094 HTTP_BODY_PARSER *Parser;\r
1095 \r
1096 if (BodyLength == 0 || Body == NULL) {\r
1097 return EFI_INVALID_PARAMETER;\r
1098 }\r
1099\r
1100 if (MsgParser == NULL) {\r
1101 return EFI_INVALID_PARAMETER;\r
1102 }\r
1103\r
1104 Parser = (HTTP_BODY_PARSER*) MsgParser;\r
1105\r
1106 if (Parser->IgnoreBody) {\r
1107 Parser->State = BodyParserComplete;\r
1108 if (Parser->Callback != NULL) {\r
1109 Status = Parser->Callback (\r
1110 BodyParseEventOnComplete,\r
1111 Body,\r
1112 0,\r
1113 Parser->Context\r
1114 );\r
1115 if (EFI_ERROR (Status)) {\r
1116 return Status;\r
1117 }\r
1118 }\r
1119 return EFI_SUCCESS;\r
1120 }\r
1121\r
1122 if (Parser->State == BodyParserBodyStart) {\r
1123 Parser->ParsedBodyLength = 0;\r
1124 if (Parser->IsChunked) {\r
1125 Parser->State = BodyParserChunkSizeStart;\r
1126 } else {\r
1127 Parser->State = BodyParserBodyIdentity;\r
1128 }\r
1129 }\r
1130\r
1131 //\r
1132 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.\r
1133 //\r
1134 for (Char = Body; Char < Body + BodyLength; ) {\r
1135\r
1136 switch (Parser->State) {\r
1137 case BodyParserStateMax:\r
1138 return EFI_ABORTED;\r
1139\r
1140 case BodyParserComplete:\r
1141 if (Parser->Callback != NULL) {\r
1142 Status = Parser->Callback (\r
1143 BodyParseEventOnComplete,\r
1144 Char,\r
1145 0,\r
1146 Parser->Context\r
1147 );\r
1148 if (EFI_ERROR (Status)) {\r
1149 return Status;\r
1150 }\r
1151 }\r
1152 return EFI_SUCCESS;\r
1153 \r
1154 case BodyParserBodyIdentity:\r
1155 //\r
1156 // Identity transfer-coding, just notify user to save the body data.\r
1157 //\r
1158 if (Parser->Callback != NULL) {\r
1159 Status = Parser->Callback (\r
1160 BodyParseEventOnData,\r
1161 Char,\r
1162 MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),\r
1163 Parser->Context\r
1164 );\r
1165 if (EFI_ERROR (Status)) {\r
1166 return Status;\r
1167 }\r
1168 }\r
1169 Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);\r
1170 Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);\r
1171 if (Parser->ParsedBodyLength == Parser->ContentLength) {\r
1172 Parser->State = BodyParserComplete;\r
1173 }\r
1174 break;\r
1175\r
1176 case BodyParserChunkSizeStart:\r
1177 //\r
1178 // First byte of chunk-size, the chunk-size might be truncated.\r
1179 //\r
1180 Parser->CurrentChunkSize = 0;\r
1181 Parser->State = BodyParserChunkSize;\r
1182 case BodyParserChunkSize:\r
1183 if (!NET_IS_HEX_CHAR (*Char)) {\r
1184 if (*Char == ';') {\r
1185 Parser->State = BodyParserChunkExtStart;\r
1186 Char++;\r
1187 } else if (*Char == '\r') {\r
1188 Parser->State = BodyParserChunkSizeEndCR;\r
1189 Char++;\r
1190 } else {\r
1191 Parser->State = BodyParserStateMax;\r
1192 }\r
1193 break;\r
1194 }\r
1195\r
1196 if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {\r
1197 return EFI_INVALID_PARAMETER;\r
1198 }\r
1199 Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);\r
1200 Char++;\r
1201 break;\r
1202\r
1203 case BodyParserChunkExtStart:\r
1204 //\r
1205 // Ignore all the chunk extensions.\r
1206 //\r
1207 if (*Char == '\r') {\r
1208 Parser->State = BodyParserChunkSizeEndCR;\r
1209 }\r
1210 Char++;\r
1211 break;\r
1212 \r
1213 case BodyParserChunkSizeEndCR:\r
1214 if (*Char != '\n') {\r
1215 Parser->State = BodyParserStateMax;\r
1216 break;\r
1217 }\r
1218 Char++;\r
1219 if (Parser->CurrentChunkSize == 0) {\r
1220 //\r
1221 // The last chunk has been parsed and now assumed the state \r
1222 // of HttpBodyParse is ParserLastCRLF. So it need to decide\r
1223 // whether the rest message is trailer or last CRLF in the next round.\r
1224 //\r
1225 Parser->ContentLengthIsValid = TRUE;\r
1226 Parser->State = BodyParserLastCRLF;\r
1227 break;\r
1228 }\r
1229 Parser->State = BodyParserChunkDataStart;\r
1230 Parser->CurrentChunkParsedSize = 0;\r
1231 break;\r
1232 \r
1233 case BodyParserLastCRLF:\r
1234 //\r
1235 // Judge the byte is belong to the Last CRLF or trailer, and then \r
1236 // configure the state of HttpBodyParse to corresponding state.\r
1237 //\r
1238 if (*Char == '\r') {\r
1239 Char++;\r
1240 Parser->State = BodyParserLastCRLFEnd;\r
1241 break;\r
1242 } else {\r
1243 Parser->State = BodyParserTrailer;\r
1244 break;\r
1245 }\r
1246 \r
1247 case BodyParserLastCRLFEnd:\r
1248 if (*Char == '\n') {\r
1249 Parser->State = BodyParserComplete;\r
1250 break;\r
1251 } else {\r
1252 Parser->State = BodyParserStateMax;\r
1253 break;\r
1254 }\r
1255 \r
1256 case BodyParserTrailer:\r
1257 if (*Char == '\r') {\r
1258 Parser->State = BodyParserChunkSizeEndCR;\r
1259 }\r
1260 Char++;\r
1261 break; \r
1262\r
1263 case BodyParserChunkDataStart:\r
1264 //\r
1265 // First byte of chunk-data, the chunk data also might be truncated.\r
1266 //\r
1267 RemainderLengthInThis = BodyLength - (Char - Body);\r
1268 LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);\r
1269 if (Parser->Callback != NULL) {\r
1270 Status = Parser->Callback (\r
1271 BodyParseEventOnData,\r
1272 Char,\r
1273 LengthForCallback,\r
1274 Parser->Context\r
1275 );\r
1276 if (EFI_ERROR (Status)) {\r
1277 return Status;\r
1278 }\r
1279 }\r
1280 Char += LengthForCallback;\r
1281 Parser->ContentLength += LengthForCallback;\r
1282 Parser->CurrentChunkParsedSize += LengthForCallback;\r
1283 if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {\r
1284 Parser->State = BodyParserChunkDataEnd;\r
1285 } \r
1286 break;\r
1287\r
1288 case BodyParserChunkDataEnd:\r
1289 if (*Char == '\r') {\r
1290 Parser->State = BodyParserChunkDataEndCR;\r
1291 } else {\r
1292 Parser->State = BodyParserStateMax;\r
1293 }\r
1294 Char++;\r
1295 break;\r
1296\r
1297 case BodyParserChunkDataEndCR:\r
1298 if (*Char != '\n') {\r
1299 Parser->State = BodyParserStateMax;\r
1300 break;\r
1301 }\r
1302 Char++;\r
1303 Parser->State = BodyParserChunkSizeStart;\r
1304 break; \r
1305\r
1306 default:\r
1307 break;\r
1308 }\r
1309\r
1310 }\r
1311\r
1312 if (Parser->State == BodyParserStateMax) {\r
1313 return EFI_ABORTED;\r
1314 }\r
1315\r
1316 return EFI_SUCCESS;\r
1317}\r
1318\r
1319/**\r
1320 Check whether the message-body is complete or not.\r
1321\r
1322 @param[in] MsgParser Pointer to the message parser.\r
1323\r
1324 @retval TRUE Message-body is complete.\r
1325 @retval FALSE Message-body is not complete.\r
1326\r
1327**/\r
1328BOOLEAN\r
1329EFIAPI\r
1330HttpIsMessageComplete (\r
1331 IN VOID *MsgParser\r
1332 )\r
1333{\r
1334 HTTP_BODY_PARSER *Parser;\r
1335\r
1336 Parser = (HTTP_BODY_PARSER*) MsgParser;\r
1337\r
1338 if (Parser->State == BodyParserComplete) {\r
1339 return TRUE;\r
1340 }\r
1341 return FALSE;\r
1342}\r
1343\r
1344/**\r
1345 Get the content length of the entity.\r
1346\r
1347 Note that in trunk transfer, the entity length is not valid until the whole message body is received.\r
1348\r
1349 @param[in] MsgParser Pointer to the message parser.\r
1350 @param[out] ContentLength Pointer to store the length of the entity.\r
1351\r
1352 @retval EFI_SUCCESS Successfully to get the entity length.\r
1353 @retval EFI_NOT_READY Entity length is not valid yet.\r
1354 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.\r
1355 \r
1356**/\r
1357EFI_STATUS\r
1358EFIAPI\r
1359HttpGetEntityLength (\r
1360 IN VOID *MsgParser,\r
1361 OUT UINTN *ContentLength\r
1362 )\r
1363{\r
1364 HTTP_BODY_PARSER *Parser;\r
1365\r
1366 if (MsgParser == NULL || ContentLength == NULL) {\r
1367 return EFI_INVALID_PARAMETER;\r
1368 }\r
1369\r
1370 Parser = (HTTP_BODY_PARSER*) MsgParser;\r
1371\r
1372 if (!Parser->ContentLengthIsValid) {\r
1373 return EFI_NOT_READY;\r
1374 }\r
1375\r
1376 *ContentLength = Parser->ContentLength;\r
1377 return EFI_SUCCESS;\r
1378}\r
1379\r
1380/**\r
1381 Release the resource of the message parser.\r
1382\r
1383 @param[in] MsgParser Pointer to the message parser.\r
1384 \r
1385**/\r
1386VOID\r
1387EFIAPI\r
1388HttpFreeMsgParser (\r
1389 IN VOID *MsgParser\r
1390 )\r
1391{\r
1392 FreePool (MsgParser);\r
1393}\r