BaseTools:Change the path of the file that Binary Cache
[mirror_edk2.git] / MdeModulePkg / Library / DxeHttpLib / DxeHttpLib.c
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
5 Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>\r
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8 \r
9 **/\r
10 \r
11 #include "DxeHttpLib.h"\r
12 \r
13 \r
14 \r
15 /**\r
16   Decode a percent-encoded URI component to the ASCII character.\r
17 \r
18   Decode the input component in Buffer according to RFC 3986. The caller is responsible to make\r
19   sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))\r
20   in bytes.\r
21 \r
22   @param[in]    Buffer           The pointer to a percent-encoded URI component.\r
23   @param[in]    BufferLength     Length of Buffer in bytes.\r
24   @param[out]   ResultBuffer     Point to the buffer to store the decode result.\r
25   @param[out]   ResultLength     Length of decoded string in ResultBuffer in bytes.\r
26 \r
27   @retval EFI_SUCCESS            Successfully decoded the URI.\r
28   @retval EFI_INVALID_PARAMETER  Buffer is not a valid percent-encoded string.\r
29 \r
30 **/\r
31 EFI_STATUS\r
32 EFIAPI\r
33 UriPercentDecode (\r
34   IN      CHAR8            *Buffer,\r
35   IN      UINT32            BufferLength,\r
36      OUT  CHAR8            *ResultBuffer,\r
37      OUT  UINT32           *ResultLength\r
38   )\r
39 {\r
40   UINTN           Index;\r
41   UINTN           Offset;\r
42   CHAR8           HexStr[3];\r
43 \r
44   if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {\r
45     return EFI_INVALID_PARAMETER;\r
46   }\r
47 \r
48   Index = 0;\r
49   Offset = 0;\r
50   HexStr[2] = '\0';\r
51   while (Index < BufferLength) {\r
52     if (Buffer[Index] == '%') {\r
53       if (Index + 1 >= BufferLength || Index + 2 >= BufferLength ||\r
54           !NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {\r
55         return EFI_INVALID_PARAMETER;\r
56       }\r
57       HexStr[0] = Buffer[Index+1];\r
58       HexStr[1] = Buffer[Index+2];\r
59       ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);\r
60       Index += 3;\r
61     } else {\r
62       ResultBuffer[Offset] = Buffer[Index];\r
63       Index++;\r
64     }\r
65     Offset++;\r
66   }\r
67 \r
68   *ResultLength = (UINT32) Offset;\r
69 \r
70   return EFI_SUCCESS;\r
71 }\r
72 \r
73 /**\r
74   This function return the updated state according to the input state and next character of\r
75   the authority.\r
76 \r
77   @param[in]       Char           Next character.\r
78   @param[in]       State          Current value of the parser state machine.\r
79   @param[in]       IsRightBracket TRUE if there is an sign ']' in the authority component and\r
80                                   indicates the next part is ':' before Port.\r
81 \r
82   @return          Updated state value.\r
83 **/\r
84 HTTP_URL_PARSE_STATE\r
85 NetHttpParseAuthorityChar (\r
86   IN  CHAR8                  Char,\r
87   IN  HTTP_URL_PARSE_STATE   State,\r
88   IN  BOOLEAN                *IsRightBracket\r
89   )\r
90 {\r
91 \r
92   //\r
93   // RFC 3986:\r
94   // The authority component is preceded by a double slash ("//") and is\r
95   // terminated by the next slash ("/"), question mark ("?"), or number\r
96   // sign ("#") character, or by the end of the URI.\r
97   //\r
98   if (Char == ' ' || Char == '\r' || Char == '\n') {\r
99     return UrlParserStateMax;\r
100   }\r
101 \r
102   //\r
103   // authority   = [ userinfo "@" ] host [ ":" port ]\r
104   //\r
105   switch (State) {\r
106   case UrlParserUserInfo:\r
107     if (Char == '@') {\r
108       return UrlParserHostStart;\r
109     }\r
110     break;\r
111 \r
112   case UrlParserHost:\r
113   case UrlParserHostStart:\r
114     if (Char == '[') {\r
115       return UrlParserHostIpv6;\r
116     }\r
117 \r
118     if (Char == ':') {\r
119       return UrlParserPortStart;\r
120     }\r
121 \r
122     return UrlParserHost;\r
123 \r
124   case UrlParserHostIpv6:\r
125     if (Char == ']') {\r
126       *IsRightBracket = TRUE;\r
127     }\r
128 \r
129     if (Char == ':' && *IsRightBracket) {\r
130       return UrlParserPortStart;\r
131     }\r
132     return UrlParserHostIpv6;\r
133 \r
134   case UrlParserPort:\r
135   case UrlParserPortStart:\r
136     return UrlParserPort;\r
137 \r
138   default:\r
139     break;\r
140   }\r
141 \r
142   return State;\r
143 }\r
144 \r
145 /**\r
146   This function parse the authority component of the input URL and update the parser.\r
147 \r
148   @param[in]       Url            The pointer to a HTTP URL string.\r
149   @param[in]       FoundAt        TRUE if there is an at sign ('@') in the authority component.\r
150   @param[in, out]  UrlParser      Pointer to the buffer of the parse result.\r
151 \r
152   @retval EFI_SUCCESS             Successfully parse the authority.\r
153   @retval EFI_INVALID_PARAMETER   The Url is invalid to parse the authority component.\r
154 \r
155 **/\r
156 EFI_STATUS\r
157 NetHttpParseAuthority (\r
158   IN      CHAR8              *Url,\r
159   IN      BOOLEAN            FoundAt,\r
160   IN OUT  HTTP_URL_PARSER    *UrlParser\r
161   )\r
162 {\r
163   CHAR8                 *Char;\r
164   CHAR8                 *Authority;\r
165   UINT32                Length;\r
166   HTTP_URL_PARSE_STATE  State;\r
167   UINT32                Field;\r
168   UINT32                OldField;\r
169   BOOLEAN               IsrightBracket;\r
170 \r
171   ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);\r
172 \r
173   //\r
174   // authority   = [ userinfo "@" ] host [ ":" port ]\r
175   //\r
176   if (FoundAt) {\r
177     State = UrlParserUserInfo;\r
178   } else {\r
179     State = UrlParserHost;\r
180   }\r
181 \r
182   IsrightBracket = FALSE;\r
183   Field = HTTP_URI_FIELD_MAX;\r
184   OldField = Field;\r
185   Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;\r
186   Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;\r
187   for (Char = Authority; Char < Authority + Length; Char++) {\r
188     State = NetHttpParseAuthorityChar (*Char, State, &IsrightBracket);\r
189     switch (State) {\r
190     case UrlParserStateMax:\r
191       return EFI_INVALID_PARAMETER;\r
192 \r
193     case UrlParserHostStart:\r
194     case UrlParserPortStart:\r
195       continue;\r
196 \r
197     case UrlParserUserInfo:\r
198       Field = HTTP_URI_FIELD_USERINFO;\r
199       break;\r
200 \r
201     case UrlParserHost:\r
202       Field = HTTP_URI_FIELD_HOST;\r
203       break;\r
204 \r
205     case UrlParserHostIpv6:\r
206       Field = HTTP_URI_FIELD_HOST;\r
207       break;\r
208 \r
209     case UrlParserPort:\r
210       Field = HTTP_URI_FIELD_PORT;\r
211       break;\r
212 \r
213     default:\r
214       ASSERT (FALSE);\r
215     }\r
216 \r
217     //\r
218     // Field not changed, count the length.\r
219     //\r
220     ASSERT (Field < HTTP_URI_FIELD_MAX);\r
221     if (Field == OldField) {\r
222       UrlParser->FieldData[Field].Length++;\r
223       continue;\r
224     }\r
225 \r
226     //\r
227     // New field start\r
228     //\r
229     UrlParser->FieldBitMap |= BIT (Field);\r
230     UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);\r
231     UrlParser->FieldData[Field].Length = 1;\r
232     OldField = Field;\r
233   }\r
234 \r
235   return EFI_SUCCESS;\r
236 }\r
237 \r
238 /**\r
239   This function return the updated state according to the input state and next character of a URL.\r
240 \r
241   @param[in]       Char           Next character.\r
242   @param[in]       State          Current value of the parser state machine.\r
243 \r
244   @return          Updated state value.\r
245 \r
246 **/\r
247 HTTP_URL_PARSE_STATE\r
248 NetHttpParseUrlChar (\r
249   IN  CHAR8                  Char,\r
250   IN  HTTP_URL_PARSE_STATE   State\r
251   )\r
252 {\r
253   if (Char == ' ' || Char == '\r' || Char == '\n') {\r
254     return UrlParserStateMax;\r
255   }\r
256 \r
257   //\r
258   // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]\r
259   //\r
260   // Request-URI    = "*" | absolute-URI | path-absolute | authority\r
261   //\r
262   // absolute-URI  = scheme ":" hier-part [ "?" query ]\r
263   // path-absolute = "/" [ segment-nz *( "/" segment ) ]\r
264   // authority   = [ userinfo "@" ] host [ ":" port ]\r
265   //\r
266   switch (State) {\r
267   case UrlParserUrlStart:\r
268     if (Char == '*' || Char == '/') {\r
269       return UrlParserPath;\r
270     }\r
271     return UrlParserScheme;\r
272 \r
273   case UrlParserScheme:\r
274     if (Char == ':') {\r
275       return UrlParserSchemeColon;\r
276     }\r
277     break;\r
278 \r
279   case UrlParserSchemeColon:\r
280     if (Char == '/') {\r
281       return UrlParserSchemeColonSlash;\r
282     }\r
283     break;\r
284 \r
285   case UrlParserSchemeColonSlash:\r
286     if (Char == '/') {\r
287       return UrlParserSchemeColonSlashSlash;\r
288     }\r
289     break;\r
290 \r
291   case UrlParserAtInAuthority:\r
292     if (Char == '@') {\r
293       return UrlParserStateMax;\r
294     }\r
295 \r
296   case UrlParserAuthority:\r
297   case UrlParserSchemeColonSlashSlash:\r
298     if (Char == '@') {\r
299       return UrlParserAtInAuthority;\r
300     }\r
301     if (Char == '/') {\r
302       return UrlParserPath;\r
303     }\r
304     if (Char == '?') {\r
305       return UrlParserQueryStart;\r
306     }\r
307     if (Char == '#') {\r
308       return UrlParserFragmentStart;\r
309     }\r
310     return UrlParserAuthority;\r
311 \r
312   case UrlParserPath:\r
313     if (Char == '?') {\r
314       return UrlParserQueryStart;\r
315     }\r
316     if (Char == '#') {\r
317       return UrlParserFragmentStart;\r
318     }\r
319     break;\r
320 \r
321   case UrlParserQuery:\r
322   case UrlParserQueryStart:\r
323     if (Char == '#') {\r
324       return UrlParserFragmentStart;\r
325     }\r
326     return UrlParserQuery;\r
327 \r
328   case UrlParserFragmentStart:\r
329     return UrlParserFragment;\r
330 \r
331   default:\r
332     break;\r
333   }\r
334 \r
335   return State;\r
336 }\r
337 /**\r
338   Create a URL parser for the input URL string.\r
339 \r
340   This function will parse and dereference the input HTTP URL into it components. The original\r
341   content of the URL won't be modified and the result will be returned in UrlParser, which can\r
342   be used in other functions like NetHttpUrlGetHostName().\r
343 \r
344   @param[in]    Url                The pointer to a HTTP URL string.\r
345   @param[in]    Length             Length of Url in bytes.\r
346   @param[in]    IsConnectMethod    Whether the Url is used in HTTP CONNECT method or not.\r
347   @param[out]   UrlParser          Pointer to the returned buffer to store the parse result.\r
348 \r
349   @retval EFI_SUCCESS              Successfully dereferenced the HTTP URL.\r
350   @retval EFI_INVALID_PARAMETER    UrlParser is NULL or Url is not a valid HTTP URL.\r
351   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
352 \r
353 **/\r
354 EFI_STATUS\r
355 EFIAPI\r
356 HttpParseUrl (\r
357   IN      CHAR8              *Url,\r
358   IN      UINT32             Length,\r
359   IN      BOOLEAN            IsConnectMethod,\r
360      OUT  VOID               **UrlParser\r
361   )\r
362 {\r
363   HTTP_URL_PARSE_STATE  State;\r
364   CHAR8                 *Char;\r
365   UINT32                Field;\r
366   UINT32                OldField;\r
367   BOOLEAN               FoundAt;\r
368   EFI_STATUS            Status;\r
369   HTTP_URL_PARSER       *Parser;\r
370 \r
371   Parser = NULL;\r
372 \r
373   if (Url == NULL || Length == 0 || UrlParser == NULL) {\r
374     return EFI_INVALID_PARAMETER;\r
375   }\r
376 \r
377   Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));\r
378   if (Parser == NULL) {\r
379     return EFI_OUT_OF_RESOURCES;\r
380   }\r
381 \r
382   if (IsConnectMethod) {\r
383     //\r
384     // According to RFC 2616, the authority form is only used by the CONNECT method.\r
385     //\r
386     State = UrlParserAuthority;\r
387   } else {\r
388     State = UrlParserUrlStart;\r
389   }\r
390 \r
391   Field = HTTP_URI_FIELD_MAX;\r
392   OldField = Field;\r
393   FoundAt = FALSE;\r
394   for (Char = Url; Char < Url + Length; Char++) {\r
395     //\r
396     // Update state machine according to next char.\r
397     //\r
398     State = NetHttpParseUrlChar (*Char, State);\r
399 \r
400     switch (State) {\r
401     case UrlParserStateMax:\r
402       FreePool (Parser);\r
403       return EFI_INVALID_PARAMETER;\r
404 \r
405     case UrlParserSchemeColon:\r
406     case UrlParserSchemeColonSlash:\r
407     case UrlParserSchemeColonSlashSlash:\r
408     case UrlParserQueryStart:\r
409     case UrlParserFragmentStart:\r
410       //\r
411       // Skip all the delimiting char: "://" "?" "@"\r
412       //\r
413       continue;\r
414 \r
415     case UrlParserScheme:\r
416       Field = HTTP_URI_FIELD_SCHEME;\r
417       break;\r
418 \r
419     case UrlParserAtInAuthority:\r
420       FoundAt = TRUE;\r
421     case UrlParserAuthority:\r
422       Field = HTTP_URI_FIELD_AUTHORITY;\r
423       break;\r
424 \r
425     case UrlParserPath:\r
426       Field = HTTP_URI_FIELD_PATH;\r
427       break;\r
428 \r
429     case UrlParserQuery:\r
430       Field = HTTP_URI_FIELD_QUERY;\r
431       break;\r
432 \r
433     case UrlParserFragment:\r
434       Field = HTTP_URI_FIELD_FRAGMENT;\r
435       break;\r
436 \r
437     default:\r
438       ASSERT (FALSE);\r
439     }\r
440 \r
441     //\r
442     // Field not changed, count the length.\r
443     //\r
444     ASSERT (Field < HTTP_URI_FIELD_MAX);\r
445     if (Field == OldField) {\r
446       Parser->FieldData[Field].Length++;\r
447       continue;\r
448     }\r
449 \r
450     //\r
451     // New field start\r
452     //\r
453     Parser->FieldBitMap |= BIT (Field);\r
454     Parser->FieldData[Field].Offset = (UINT32) (Char - Url);\r
455     Parser->FieldData[Field].Length = 1;\r
456     OldField = Field;\r
457   }\r
458 \r
459   //\r
460   // If has authority component, continue to parse the username, host and port.\r
461   //\r
462   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {\r
463     Status = NetHttpParseAuthority (Url, FoundAt, Parser);\r
464     if (EFI_ERROR (Status)) {\r
465       FreePool (Parser);\r
466       return Status;\r
467     }\r
468   }\r
469 \r
470   *UrlParser = Parser;\r
471   return EFI_SUCCESS;\r
472 }\r
473 \r
474 /**\r
475   Get the Hostname from a HTTP URL.\r
476 \r
477   This function will return the HostName according to the Url and previous parse result ,and\r
478   it is the caller's responsibility to free the buffer returned in *HostName.\r
479 \r
480   @param[in]    Url                The pointer to a HTTP URL string.\r
481   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().\r
482   @param[out]   HostName           Pointer to a buffer to store the HostName.\r
483 \r
484   @retval EFI_SUCCESS              Successfully get the required component.\r
485   @retval EFI_INVALID_PARAMETER    Uri is NULL or HostName is NULL or UrlParser is invalid.\r
486   @retval EFI_NOT_FOUND            No hostName component in the URL.\r
487   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
488 \r
489 **/\r
490 EFI_STATUS\r
491 EFIAPI\r
492 HttpUrlGetHostName (\r
493   IN      CHAR8              *Url,\r
494   IN      VOID               *UrlParser,\r
495      OUT  CHAR8              **HostName\r
496   )\r
497 {\r
498   CHAR8                *Name;\r
499   EFI_STATUS           Status;\r
500   UINT32               ResultLength;\r
501   HTTP_URL_PARSER      *Parser;\r
502 \r
503   if (Url == NULL || UrlParser == NULL || HostName == NULL) {\r
504     return EFI_INVALID_PARAMETER;\r
505   }\r
506 \r
507   Parser = (HTTP_URL_PARSER *) UrlParser;\r
508 \r
509   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
510     return EFI_NOT_FOUND;\r
511   }\r
512 \r
513   Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
514   if (Name == NULL) {\r
515     return EFI_OUT_OF_RESOURCES;\r
516   }\r
517 \r
518   Status = UriPercentDecode (\r
519              Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
520              Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
521              Name,\r
522              &ResultLength\r
523              );\r
524   if (EFI_ERROR (Status)) {\r
525     FreePool (Name);\r
526     return Status;\r
527   }\r
528 \r
529   Name[ResultLength] = '\0';\r
530   *HostName = Name;\r
531   return EFI_SUCCESS;\r
532 }\r
533 \r
534 \r
535 /**\r
536   Get the IPv4 address from a HTTP URL.\r
537 \r
538   This function will return the IPv4 address according to the Url and previous parse result.\r
539 \r
540   @param[in]    Url                The pointer to a HTTP URL string.\r
541   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().\r
542   @param[out]   Ip4Address         Pointer to a buffer to store the IP address.\r
543 \r
544   @retval EFI_SUCCESS              Successfully get the required component.\r
545   @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip4Address is NULL or UrlParser is invalid.\r
546   @retval EFI_NOT_FOUND            No IPv4 address component in the URL.\r
547   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
548 \r
549 **/\r
550 EFI_STATUS\r
551 EFIAPI\r
552 HttpUrlGetIp4 (\r
553   IN      CHAR8              *Url,\r
554   IN      VOID               *UrlParser,\r
555      OUT  EFI_IPv4_ADDRESS   *Ip4Address\r
556   )\r
557 {\r
558   CHAR8                *Ip4String;\r
559   EFI_STATUS           Status;\r
560   UINT32               ResultLength;\r
561   HTTP_URL_PARSER      *Parser;\r
562 \r
563   if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {\r
564     return EFI_INVALID_PARAMETER;\r
565   }\r
566 \r
567   Parser = (HTTP_URL_PARSER *) UrlParser;\r
568 \r
569   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
570     return EFI_NOT_FOUND;\r
571   }\r
572 \r
573   Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
574   if (Ip4String == NULL) {\r
575     return EFI_OUT_OF_RESOURCES;\r
576   }\r
577 \r
578   Status = UriPercentDecode (\r
579              Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
580              Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
581              Ip4String,\r
582              &ResultLength\r
583              );\r
584   if (EFI_ERROR (Status)) {\r
585     FreePool (Ip4String);\r
586     return Status;\r
587   }\r
588 \r
589   Ip4String[ResultLength] = '\0';\r
590   Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);\r
591   FreePool (Ip4String);\r
592 \r
593   return Status;\r
594 }\r
595 \r
596 /**\r
597   Get the IPv6 address from a HTTP URL.\r
598 \r
599   This function will return the IPv6 address according to the Url and previous parse result.\r
600 \r
601   @param[in]    Url                The pointer to a HTTP URL string.\r
602   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().\r
603   @param[out]   Ip6Address         Pointer to a buffer to store the IP address.\r
604 \r
605   @retval EFI_SUCCESS              Successfully get the required component.\r
606   @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip6Address is NULL or UrlParser is invalid.\r
607   @retval EFI_NOT_FOUND            No IPv6 address component in the URL.\r
608   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
609 \r
610 **/\r
611 EFI_STATUS\r
612 EFIAPI\r
613 HttpUrlGetIp6 (\r
614   IN      CHAR8              *Url,\r
615   IN      VOID               *UrlParser,\r
616      OUT  EFI_IPv6_ADDRESS   *Ip6Address\r
617   )\r
618 {\r
619   CHAR8                *Ip6String;\r
620   CHAR8                *Ptr;\r
621   UINT32               Length;\r
622   EFI_STATUS           Status;\r
623   UINT32               ResultLength;\r
624   HTTP_URL_PARSER      *Parser;\r
625 \r
626   if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {\r
627     return EFI_INVALID_PARAMETER;\r
628   }\r
629 \r
630   Parser = (HTTP_URL_PARSER *) UrlParser;\r
631 \r
632   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
633     return EFI_NOT_FOUND;\r
634   }\r
635 \r
636   //\r
637   // IP-literal = "[" ( IPv6address / IPvFuture  ) "]"\r
638   //\r
639   Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;\r
640   if (Length < 2) {\r
641     return EFI_INVALID_PARAMETER;\r
642   }\r
643 \r
644   Ptr    = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;\r
645   if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {\r
646     return EFI_INVALID_PARAMETER;\r
647   }\r
648 \r
649   Ip6String = AllocatePool (Length);\r
650   if (Ip6String == NULL) {\r
651     return EFI_OUT_OF_RESOURCES;\r
652   }\r
653 \r
654   Status = UriPercentDecode (\r
655              Ptr + 1,\r
656              Length - 2,\r
657              Ip6String,\r
658              &ResultLength\r
659              );\r
660   if (EFI_ERROR (Status)) {\r
661     FreePool (Ip6String);\r
662     return Status;\r
663   }\r
664 \r
665   Ip6String[ResultLength] = '\0';\r
666   Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);\r
667   FreePool (Ip6String);\r
668 \r
669   return Status;\r
670 }\r
671 \r
672 /**\r
673   Get the port number from a HTTP URL.\r
674 \r
675   This function will return the port number according to the Url and previous parse result.\r
676 \r
677   @param[in]    Url                The pointer to a HTTP URL string.\r
678   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().\r
679   @param[out]   Port               Pointer to a buffer to store the port number.\r
680 \r
681   @retval EFI_SUCCESS              Successfully get the required component.\r
682   @retval EFI_INVALID_PARAMETER    Uri is NULL or Port is NULL or UrlParser is invalid.\r
683   @retval EFI_NOT_FOUND            No port number in the URL.\r
684   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
685 \r
686 **/\r
687 EFI_STATUS\r
688 EFIAPI\r
689 HttpUrlGetPort (\r
690   IN      CHAR8              *Url,\r
691   IN      VOID               *UrlParser,\r
692      OUT  UINT16             *Port\r
693   )\r
694 {\r
695   CHAR8                *PortString;\r
696   EFI_STATUS           Status;\r
697   UINTN                Index;\r
698   UINTN                Data;\r
699   UINT32               ResultLength;\r
700   HTTP_URL_PARSER      *Parser;\r
701 \r
702   if (Url == NULL || UrlParser == NULL || Port == NULL) {\r
703     return EFI_INVALID_PARAMETER;\r
704   }\r
705 \r
706   *Port = 0;\r
707   Index = 0;\r
708 \r
709   Parser = (HTTP_URL_PARSER *) UrlParser;\r
710 \r
711   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {\r
712     return EFI_NOT_FOUND;\r
713   }\r
714 \r
715   PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);\r
716   if (PortString == NULL) {\r
717     return EFI_OUT_OF_RESOURCES;\r
718   }\r
719 \r
720   Status = UriPercentDecode (\r
721              Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,\r
722              Parser->FieldData[HTTP_URI_FIELD_PORT].Length,\r
723              PortString,\r
724              &ResultLength\r
725              );\r
726   if (EFI_ERROR (Status)) {\r
727     goto ON_EXIT;\r
728   }\r
729 \r
730   PortString[ResultLength] = '\0';\r
731 \r
732   while (Index < ResultLength) {\r
733     if (!NET_IS_DIGIT (PortString[Index])) {\r
734       Status = EFI_INVALID_PARAMETER;\r
735       goto ON_EXIT;\r
736     }\r
737     Index ++;\r
738   }\r
739 \r
740   Status =  AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **) NULL, &Data);\r
741 \r
742   if (Data > HTTP_URI_PORT_MAX_NUM) {\r
743     Status = EFI_INVALID_PARAMETER;\r
744     goto ON_EXIT;\r
745   }\r
746 \r
747   *Port = (UINT16) Data;\r
748 \r
749 ON_EXIT:\r
750   FreePool (PortString);\r
751   return Status;\r
752 }\r
753 \r
754 /**\r
755   Get the Path from a HTTP URL.\r
756 \r
757   This function will return the Path according to the Url and previous parse result,and\r
758   it is the caller's responsibility to free the buffer returned in *Path.\r
759 \r
760   @param[in]    Url                The pointer to a HTTP URL string.\r
761   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().\r
762   @param[out]   Path               Pointer to a buffer to store the Path.\r
763 \r
764   @retval EFI_SUCCESS              Successfully get the required component.\r
765   @retval EFI_INVALID_PARAMETER    Uri is NULL or HostName is NULL or UrlParser is invalid.\r
766   @retval EFI_NOT_FOUND            No hostName component in the URL.\r
767   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
768 \r
769 **/\r
770 EFI_STATUS\r
771 EFIAPI\r
772 HttpUrlGetPath (\r
773   IN      CHAR8              *Url,\r
774   IN      VOID               *UrlParser,\r
775      OUT  CHAR8              **Path\r
776   )\r
777 {\r
778   CHAR8                *PathStr;\r
779   EFI_STATUS           Status;\r
780   UINT32               ResultLength;\r
781   HTTP_URL_PARSER      *Parser;\r
782 \r
783   if (Url == NULL || UrlParser == NULL || Path == NULL) {\r
784     return EFI_INVALID_PARAMETER;\r
785   }\r
786 \r
787   Parser = (HTTP_URL_PARSER *) UrlParser;\r
788 \r
789   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {\r
790     return EFI_NOT_FOUND;\r
791   }\r
792 \r
793   PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);\r
794   if (PathStr == NULL) {\r
795     return EFI_OUT_OF_RESOURCES;\r
796   }\r
797 \r
798   Status = UriPercentDecode (\r
799              Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,\r
800              Parser->FieldData[HTTP_URI_FIELD_PATH].Length,\r
801              PathStr,\r
802              &ResultLength\r
803              );\r
804   if (EFI_ERROR (Status)) {\r
805     FreePool (PathStr);\r
806     return Status;\r
807   }\r
808 \r
809   PathStr[ResultLength] = '\0';\r
810   *Path = PathStr;\r
811   return EFI_SUCCESS;\r
812 }\r
813 \r
814 /**\r
815   Release the resource of the URL parser.\r
816 \r
817   @param[in]    UrlParser            Pointer to the parser.\r
818 \r
819 **/\r
820 VOID\r
821 EFIAPI\r
822 HttpUrlFreeParser (\r
823   IN      VOID               *UrlParser\r
824   )\r
825 {\r
826   FreePool (UrlParser);\r
827 }\r
828 \r
829 /**\r
830   Find a specified header field according to the field name.\r
831 \r
832   @param[in]   HeaderCount      Number of HTTP header structures in Headers list.\r
833   @param[in]   Headers          Array containing list of HTTP headers.\r
834   @param[in]   FieldName        Null terminated string which describes a field name.\r
835 \r
836   @return    Pointer to the found header or NULL.\r
837 \r
838 **/\r
839 EFI_HTTP_HEADER *\r
840 EFIAPI\r
841 HttpFindHeader (\r
842   IN  UINTN                HeaderCount,\r
843   IN  EFI_HTTP_HEADER      *Headers,\r
844   IN  CHAR8                *FieldName\r
845   )\r
846 {\r
847   UINTN                 Index;\r
848 \r
849   if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {\r
850     return NULL;\r
851   }\r
852 \r
853   for (Index = 0; Index < HeaderCount; Index++){\r
854     //\r
855     // Field names are case-insensitive (RFC 2616).\r
856     //\r
857     if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {\r
858       return &Headers[Index];\r
859     }\r
860   }\r
861   return NULL;\r
862 }\r
863 \r
864 typedef enum {\r
865   BodyParserBodyStart,\r
866   BodyParserBodyIdentity,\r
867   BodyParserChunkSizeStart,\r
868   BodyParserChunkSize,\r
869   BodyParserChunkSizeEndCR,\r
870   BodyParserChunkExtStart,\r
871   BodyParserChunkDataStart,\r
872   BodyParserChunkDataEnd,\r
873   BodyParserChunkDataEndCR,\r
874   BodyParserTrailer,\r
875   BodyParserLastCRLF,\r
876   BodyParserLastCRLFEnd,\r
877   BodyParserComplete,\r
878   BodyParserStateMax\r
879 } HTTP_BODY_PARSE_STATE;\r
880 \r
881 typedef struct {\r
882   BOOLEAN                       IgnoreBody;    // "MUST NOT" include a message-body\r
883   BOOLEAN                       IsChunked;     // "chunked" transfer-coding.\r
884   BOOLEAN                       ContentLengthIsValid;\r
885   UINTN                         ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE\r
886 \r
887   HTTP_BODY_PARSER_CALLBACK     Callback;\r
888   VOID                          *Context;\r
889   UINTN                         ParsedBodyLength;\r
890   HTTP_BODY_PARSE_STATE         State;\r
891   UINTN                         CurrentChunkSize;\r
892   UINTN                         CurrentChunkParsedSize;\r
893 } HTTP_BODY_PARSER;\r
894 \r
895 /**\r
896   Convert an hexadecimal char to a value of type UINTN.\r
897 \r
898   @param[in]       Char           Ascii character.\r
899 \r
900   @return          Value translated from Char.\r
901 \r
902 **/\r
903 UINTN\r
904 HttpIoHexCharToUintn (\r
905   IN CHAR8           Char\r
906   )\r
907 {\r
908   if (Char >= '0' && Char <= '9') {\r
909     return Char - '0';\r
910   }\r
911 \r
912   return (10 + AsciiCharToUpper (Char) - 'A');\r
913 }\r
914 \r
915 /**\r
916   Get the value of the content length if there is a "Content-Length" header.\r
917 \r
918   @param[in]    HeaderCount        Number of HTTP header structures in Headers.\r
919   @param[in]    Headers            Array containing list of HTTP headers.\r
920   @param[out]   ContentLength      Pointer to save the value of the content length.\r
921 \r
922   @retval EFI_SUCCESS              Successfully get the content length.\r
923   @retval EFI_NOT_FOUND            No "Content-Length" header in the Headers.\r
924 \r
925 **/\r
926 EFI_STATUS\r
927 HttpIoParseContentLengthHeader (\r
928   IN     UINTN                HeaderCount,\r
929   IN     EFI_HTTP_HEADER      *Headers,\r
930      OUT UINTN                *ContentLength\r
931   )\r
932 {\r
933   EFI_HTTP_HEADER       *Header;\r
934 \r
935   Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);\r
936   if (Header == NULL) {\r
937     return EFI_NOT_FOUND;\r
938   }\r
939 \r
940   return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);\r
941 }\r
942 \r
943 /**\r
944 \r
945   Check whether the HTTP message is using the "chunked" transfer-coding.\r
946 \r
947   @param[in]    HeaderCount        Number of HTTP header structures in Headers.\r
948   @param[in]    Headers            Array containing list of HTTP headers.\r
949 \r
950   @return       The message is "chunked" transfer-coding (TRUE) or not (FALSE).\r
951 \r
952 **/\r
953 BOOLEAN\r
954 HttpIoIsChunked (\r
955   IN   UINTN                    HeaderCount,\r
956   IN   EFI_HTTP_HEADER          *Headers\r
957   )\r
958 {\r
959   EFI_HTTP_HEADER       *Header;\r
960 \r
961 \r
962   Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);\r
963   if (Header == NULL) {\r
964     return FALSE;\r
965   }\r
966 \r
967   if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {\r
968     return TRUE;\r
969   }\r
970 \r
971   return FALSE;\r
972 }\r
973 \r
974 /**\r
975   Check whether the HTTP message should have a message-body.\r
976 \r
977   @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.\r
978   @param[in]    StatusCode         Response status code returned by the remote host.\r
979 \r
980   @return       The message should have a message-body (FALSE) or not (TRUE).\r
981 \r
982 **/\r
983 BOOLEAN\r
984 HttpIoNoMessageBody (\r
985   IN   EFI_HTTP_METHOD          Method,\r
986   IN   EFI_HTTP_STATUS_CODE     StatusCode\r
987   )\r
988 {\r
989   //\r
990   // RFC 2616:\r
991   // All responses to the HEAD request method\r
992   // MUST NOT include a message-body, even though the presence of entity-\r
993   // header fields might lead one to believe they do. All 1xx\r
994   // (informational), 204 (no content), and 304 (not modified) responses\r
995   // MUST NOT include a message-body. All other responses do include a\r
996   // message-body, although it MAY be of zero length.\r
997   //\r
998   if (Method == HttpMethodHead) {\r
999     return TRUE;\r
1000   }\r
1001 \r
1002   if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||\r
1003       (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||\r
1004       (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||\r
1005       (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))\r
1006   {\r
1007     return TRUE;\r
1008   }\r
1009 \r
1010   return FALSE;\r
1011 }\r
1012 \r
1013 /**\r
1014   Initialize a HTTP message-body parser.\r
1015 \r
1016   This function will create and initialize a HTTP message parser according to caller provided HTTP message\r
1017   header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().\r
1018 \r
1019   @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.\r
1020   @param[in]    StatusCode         Response status code returned by the remote host.\r
1021   @param[in]    HeaderCount        Number of HTTP header structures in Headers.\r
1022   @param[in]    Headers            Array containing list of HTTP headers.\r
1023   @param[in]    Callback           Callback function that is invoked when parsing the HTTP message-body,\r
1024                                    set to NULL to ignore all events.\r
1025   @param[in]    Context            Pointer to the context that will be passed to Callback.\r
1026   @param[out]   MsgParser          Pointer to the returned buffer to store the message parser.\r
1027 \r
1028   @retval EFI_SUCCESS              Successfully initialized the parser.\r
1029   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.\r
1030   @retval EFI_INVALID_PARAMETER    MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.\r
1031   @retval Others                   Failed to initialize the parser.\r
1032 \r
1033 **/\r
1034 EFI_STATUS\r
1035 EFIAPI\r
1036 HttpInitMsgParser (\r
1037   IN     EFI_HTTP_METHOD               Method,\r
1038   IN     EFI_HTTP_STATUS_CODE          StatusCode,\r
1039   IN     UINTN                         HeaderCount,\r
1040   IN     EFI_HTTP_HEADER               *Headers,\r
1041   IN     HTTP_BODY_PARSER_CALLBACK     Callback,\r
1042   IN     VOID                          *Context,\r
1043     OUT  VOID                          **MsgParser\r
1044   )\r
1045 {\r
1046   EFI_STATUS            Status;\r
1047   HTTP_BODY_PARSER      *Parser;\r
1048 \r
1049   if (HeaderCount != 0 && Headers == NULL) {\r
1050     return EFI_INVALID_PARAMETER;\r
1051   }\r
1052 \r
1053   if (MsgParser == NULL) {\r
1054     return EFI_INVALID_PARAMETER;\r
1055   }\r
1056 \r
1057   Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));\r
1058   if (Parser == NULL) {\r
1059     return EFI_OUT_OF_RESOURCES;\r
1060   }\r
1061 \r
1062   Parser->State = BodyParserBodyStart;\r
1063 \r
1064   //\r
1065   // Determine the message length according to RFC 2616.\r
1066   // 1. Check whether the message "MUST NOT" have a message-body.\r
1067   //\r
1068   Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);\r
1069   //\r
1070   // 2. Check whether the message using "chunked" transfer-coding.\r
1071   //\r
1072   Parser->IsChunked  = HttpIoIsChunked (HeaderCount, Headers);\r
1073   //\r
1074   // 3. Check whether the message has a Content-Length header field.\r
1075   //\r
1076   Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);\r
1077   if (!EFI_ERROR (Status)) {\r
1078     Parser->ContentLengthIsValid = TRUE;\r
1079   }\r
1080   //\r
1081   // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".\r
1082   // 5. By server closing the connection\r
1083   //\r
1084 \r
1085   //\r
1086   // Set state to skip body parser if the message shouldn't have a message body.\r
1087   //\r
1088   if (Parser->IgnoreBody) {\r
1089     Parser->State = BodyParserComplete;\r
1090   } else {\r
1091     Parser->Callback = Callback;\r
1092     Parser->Context  = Context;\r
1093   }\r
1094 \r
1095   *MsgParser = Parser;\r
1096   return EFI_SUCCESS;\r
1097 }\r
1098 \r
1099 /**\r
1100   Parse message body.\r
1101 \r
1102   Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.\r
1103 \r
1104   @param[in, out]    MsgParser            Pointer to the message parser.\r
1105   @param[in]         BodyLength           Length in bytes of the Body.\r
1106   @param[in]         Body                 Pointer to the buffer of the message-body to be parsed.\r
1107 \r
1108   @retval EFI_SUCCESS                Successfully parse the message-body.\r
1109   @retval EFI_INVALID_PARAMETER      MsgParser is NULL or Body is NULL or BodyLength is 0.\r
1110   @retval EFI_ABORTED                Operation aborted.\r
1111   @retval Other                      Error happened while parsing message body.\r
1112 \r
1113 **/\r
1114 EFI_STATUS\r
1115 EFIAPI\r
1116 HttpParseMessageBody (\r
1117   IN OUT VOID              *MsgParser,\r
1118   IN     UINTN             BodyLength,\r
1119   IN     CHAR8             *Body\r
1120   )\r
1121 {\r
1122   CHAR8                 *Char;\r
1123   UINTN                 RemainderLengthInThis;\r
1124   UINTN                 LengthForCallback;\r
1125   EFI_STATUS            Status;\r
1126   HTTP_BODY_PARSER      *Parser;\r
1127 \r
1128   if (BodyLength == 0 || Body == NULL) {\r
1129     return EFI_INVALID_PARAMETER;\r
1130   }\r
1131 \r
1132   if (MsgParser == NULL) {\r
1133     return EFI_INVALID_PARAMETER;\r
1134   }\r
1135 \r
1136   Parser = (HTTP_BODY_PARSER *) MsgParser;\r
1137 \r
1138   if (Parser->IgnoreBody) {\r
1139     Parser->State = BodyParserComplete;\r
1140     if (Parser->Callback != NULL) {\r
1141       Status = Parser->Callback (\r
1142                          BodyParseEventOnComplete,\r
1143                          Body,\r
1144                          0,\r
1145                          Parser->Context\r
1146                          );\r
1147       if (EFI_ERROR (Status)) {\r
1148         return Status;\r
1149       }\r
1150     }\r
1151     return EFI_SUCCESS;\r
1152   }\r
1153 \r
1154   if (Parser->State == BodyParserBodyStart) {\r
1155     Parser->ParsedBodyLength = 0;\r
1156     if (Parser->IsChunked) {\r
1157       Parser->State = BodyParserChunkSizeStart;\r
1158     } else {\r
1159       Parser->State = BodyParserBodyIdentity;\r
1160     }\r
1161   }\r
1162 \r
1163   //\r
1164   // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.\r
1165   //\r
1166   for (Char = Body; Char < Body + BodyLength; ) {\r
1167 \r
1168     switch (Parser->State) {\r
1169     case BodyParserStateMax:\r
1170       return EFI_ABORTED;\r
1171 \r
1172     case BodyParserBodyIdentity:\r
1173       //\r
1174       // Identity transfer-coding, just notify user to save the body data.\r
1175       //\r
1176       if (Parser->Callback != NULL) {\r
1177         Status = Parser->Callback (\r
1178                            BodyParseEventOnData,\r
1179                            Char,\r
1180                            MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),\r
1181                            Parser->Context\r
1182                            );\r
1183         if (EFI_ERROR (Status)) {\r
1184           return Status;\r
1185         }\r
1186       }\r
1187       Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);\r
1188       Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);\r
1189       if (Parser->ParsedBodyLength == Parser->ContentLength) {\r
1190         Parser->State = BodyParserComplete;\r
1191         if (Parser->Callback != NULL) {\r
1192           Status = Parser->Callback (\r
1193                              BodyParseEventOnComplete,\r
1194                              Char,\r
1195                              0,\r
1196                              Parser->Context\r
1197                              );\r
1198           if (EFI_ERROR (Status)) {\r
1199             return Status;\r
1200           }\r
1201         }\r
1202       }\r
1203       break;\r
1204 \r
1205     case BodyParserChunkSizeStart:\r
1206       //\r
1207       // First byte of chunk-size, the chunk-size might be truncated.\r
1208       //\r
1209       Parser->CurrentChunkSize = 0;\r
1210       Parser->State = BodyParserChunkSize;\r
1211     case BodyParserChunkSize:\r
1212       if (!NET_IS_HEX_CHAR (*Char)) {\r
1213         if (*Char == ';') {\r
1214           Parser->State = BodyParserChunkExtStart;\r
1215           Char++;\r
1216         } else if (*Char == '\r') {\r
1217           Parser->State = BodyParserChunkSizeEndCR;\r
1218           Char++;\r
1219         } else {\r
1220           Parser->State = BodyParserStateMax;\r
1221         }\r
1222         break;\r
1223       }\r
1224 \r
1225       if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {\r
1226         return EFI_INVALID_PARAMETER;\r
1227       }\r
1228       Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);\r
1229       Char++;\r
1230       break;\r
1231 \r
1232     case BodyParserChunkExtStart:\r
1233       //\r
1234       // Ignore all the chunk extensions.\r
1235       //\r
1236       if (*Char == '\r') {\r
1237         Parser->State = BodyParserChunkSizeEndCR;\r
1238        }\r
1239       Char++;\r
1240       break;\r
1241 \r
1242     case BodyParserChunkSizeEndCR:\r
1243       if (*Char != '\n') {\r
1244         Parser->State = BodyParserStateMax;\r
1245         break;\r
1246       }\r
1247       Char++;\r
1248       if (Parser->CurrentChunkSize == 0) {\r
1249         //\r
1250         // The last chunk has been parsed and now assumed the state\r
1251         // of HttpBodyParse is ParserLastCRLF. So it need to decide\r
1252         // whether the rest message is trailer or last CRLF in the next round.\r
1253         //\r
1254         Parser->ContentLengthIsValid = TRUE;\r
1255         Parser->State = BodyParserLastCRLF;\r
1256         break;\r
1257       }\r
1258       Parser->State = BodyParserChunkDataStart;\r
1259       Parser->CurrentChunkParsedSize = 0;\r
1260       break;\r
1261 \r
1262     case BodyParserLastCRLF:\r
1263       //\r
1264       // Judge the byte is belong to the Last CRLF or trailer, and then\r
1265       // configure the state of HttpBodyParse to corresponding state.\r
1266       //\r
1267       if (*Char == '\r') {\r
1268         Char++;\r
1269         Parser->State = BodyParserLastCRLFEnd;\r
1270         break;\r
1271       } else {\r
1272         Parser->State = BodyParserTrailer;\r
1273         break;\r
1274       }\r
1275 \r
1276     case BodyParserLastCRLFEnd:\r
1277       if (*Char == '\n') {\r
1278         Parser->State = BodyParserComplete;\r
1279         Char++;\r
1280         if (Parser->Callback != NULL) {\r
1281           Status = Parser->Callback (\r
1282                              BodyParseEventOnComplete,\r
1283                              Char,\r
1284                              0,\r
1285                              Parser->Context\r
1286                              );\r
1287           if (EFI_ERROR (Status)) {\r
1288             return Status;\r
1289           }\r
1290         }\r
1291         break;\r
1292       } else {\r
1293         Parser->State = BodyParserStateMax;\r
1294         break;\r
1295       }\r
1296 \r
1297     case BodyParserTrailer:\r
1298       if (*Char == '\r') {\r
1299         Parser->State = BodyParserChunkSizeEndCR;\r
1300       }\r
1301       Char++;\r
1302       break;\r
1303 \r
1304     case BodyParserChunkDataStart:\r
1305       //\r
1306       // First byte of chunk-data, the chunk data also might be truncated.\r
1307       //\r
1308       RemainderLengthInThis = BodyLength - (Char - Body);\r
1309       LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);\r
1310       if (Parser->Callback != NULL) {\r
1311         Status = Parser->Callback (\r
1312                            BodyParseEventOnData,\r
1313                            Char,\r
1314                            LengthForCallback,\r
1315                            Parser->Context\r
1316                            );\r
1317         if (EFI_ERROR (Status)) {\r
1318           return Status;\r
1319         }\r
1320       }\r
1321       Char += LengthForCallback;\r
1322       Parser->ContentLength += LengthForCallback;\r
1323       Parser->CurrentChunkParsedSize += LengthForCallback;\r
1324       if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {\r
1325         Parser->State = BodyParserChunkDataEnd;\r
1326       }\r
1327       break;\r
1328 \r
1329     case BodyParserChunkDataEnd:\r
1330       if (*Char == '\r') {\r
1331         Parser->State = BodyParserChunkDataEndCR;\r
1332       } else {\r
1333         Parser->State = BodyParserStateMax;\r
1334       }\r
1335       Char++;\r
1336       break;\r
1337 \r
1338     case BodyParserChunkDataEndCR:\r
1339       if (*Char != '\n') {\r
1340         Parser->State = BodyParserStateMax;\r
1341         break;\r
1342       }\r
1343       Char++;\r
1344       Parser->State = BodyParserChunkSizeStart;\r
1345       break;\r
1346 \r
1347     default:\r
1348       break;\r
1349     }\r
1350 \r
1351   }\r
1352 \r
1353   if (Parser->State == BodyParserStateMax) {\r
1354     return EFI_ABORTED;\r
1355   }\r
1356 \r
1357   return EFI_SUCCESS;\r
1358 }\r
1359 \r
1360 /**\r
1361   Check whether the message-body is complete or not.\r
1362 \r
1363   @param[in]    MsgParser            Pointer to the message parser.\r
1364 \r
1365   @retval TRUE                       Message-body is complete.\r
1366   @retval FALSE                      Message-body is not complete.\r
1367 \r
1368 **/\r
1369 BOOLEAN\r
1370 EFIAPI\r
1371 HttpIsMessageComplete (\r
1372   IN VOID              *MsgParser\r
1373   )\r
1374 {\r
1375   HTTP_BODY_PARSER      *Parser;\r
1376 \r
1377   if (MsgParser == NULL) {\r
1378     return FALSE;\r
1379   }\r
1380 \r
1381   Parser = (HTTP_BODY_PARSER *) MsgParser;\r
1382 \r
1383   if (Parser->State == BodyParserComplete) {\r
1384     return TRUE;\r
1385   }\r
1386   return FALSE;\r
1387 }\r
1388 \r
1389 /**\r
1390   Get the content length of the entity.\r
1391 \r
1392   Note that in trunk transfer, the entity length is not valid until the whole message body is received.\r
1393 \r
1394   @param[in]    MsgParser            Pointer to the message parser.\r
1395   @param[out]   ContentLength        Pointer to store the length of the entity.\r
1396 \r
1397   @retval EFI_SUCCESS                Successfully to get the entity length.\r
1398   @retval EFI_NOT_READY              Entity length is not valid yet.\r
1399   @retval EFI_INVALID_PARAMETER      MsgParser is NULL or ContentLength is NULL.\r
1400 \r
1401 **/\r
1402 EFI_STATUS\r
1403 EFIAPI\r
1404 HttpGetEntityLength (\r
1405   IN  VOID              *MsgParser,\r
1406   OUT UINTN             *ContentLength\r
1407   )\r
1408 {\r
1409   HTTP_BODY_PARSER      *Parser;\r
1410 \r
1411   if (MsgParser == NULL || ContentLength == NULL) {\r
1412     return EFI_INVALID_PARAMETER;\r
1413   }\r
1414 \r
1415   Parser = (HTTP_BODY_PARSER *) MsgParser;\r
1416 \r
1417   if (!Parser->ContentLengthIsValid) {\r
1418     return EFI_NOT_READY;\r
1419   }\r
1420 \r
1421   *ContentLength = Parser->ContentLength;\r
1422   return EFI_SUCCESS;\r
1423 }\r
1424 \r
1425 /**\r
1426   Release the resource of the message parser.\r
1427 \r
1428   @param[in]    MsgParser            Pointer to the message parser.\r
1429 \r
1430 **/\r
1431 VOID\r
1432 EFIAPI\r
1433 HttpFreeMsgParser (\r
1434   IN  VOID           *MsgParser\r
1435   )\r
1436 {\r
1437   FreePool (MsgParser);\r
1438 }\r
1439 \r
1440 \r
1441 /**\r
1442   Get the next string, which is distinguished by specified separator.\r
1443 \r
1444   @param[in]  String             Pointer to the string.\r
1445   @param[in]  Separator          Specified separator used to distinguish where is the beginning\r
1446                                  of next string.\r
1447 \r
1448   @return     Pointer to the next string.\r
1449   @return     NULL if not find or String is NULL.\r
1450 \r
1451 **/\r
1452 CHAR8 *\r
1453 AsciiStrGetNextToken (\r
1454   IN CONST CHAR8 *String,\r
1455   IN       CHAR8 Separator\r
1456   )\r
1457 {\r
1458   CONST CHAR8 *Token;\r
1459 \r
1460   Token = String;\r
1461   while (TRUE) {\r
1462     if (*Token == 0) {\r
1463       return NULL;\r
1464     }\r
1465     if (*Token == Separator) {\r
1466       return (CHAR8 *)(Token + 1);\r
1467     }\r
1468     Token++;\r
1469   }\r
1470 }\r
1471 \r
1472 /**\r
1473   Set FieldName and FieldValue into specified HttpHeader.\r
1474 \r
1475   @param[in,out]  HttpHeader      Specified HttpHeader.\r
1476   @param[in]  FieldName           FieldName of this HttpHeader, a NULL terminated ASCII string.\r
1477   @param[in]  FieldValue          FieldValue of this HttpHeader, a NULL terminated ASCII string.\r
1478 \r
1479 \r
1480   @retval EFI_SUCCESS             The FieldName and FieldValue are set into HttpHeader successfully.\r
1481   @retval EFI_INVALID_PARAMETER   The parameter is invalid.\r
1482   @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.\r
1483 \r
1484 **/\r
1485 EFI_STATUS\r
1486 EFIAPI\r
1487 HttpSetFieldNameAndValue (\r
1488   IN  OUT   EFI_HTTP_HEADER       *HttpHeader,\r
1489   IN  CONST CHAR8                 *FieldName,\r
1490   IN  CONST CHAR8                 *FieldValue\r
1491   )\r
1492 {\r
1493   UINTN                       FieldNameSize;\r
1494   UINTN                       FieldValueSize;\r
1495 \r
1496   if (HttpHeader == NULL || FieldName == NULL || FieldValue == NULL) {\r
1497     return EFI_INVALID_PARAMETER;\r
1498   }\r
1499 \r
1500   if (HttpHeader->FieldName != NULL) {\r
1501     FreePool (HttpHeader->FieldName);\r
1502   }\r
1503   if (HttpHeader->FieldValue != NULL) {\r
1504     FreePool (HttpHeader->FieldValue);\r
1505   }\r
1506 \r
1507   FieldNameSize = AsciiStrSize (FieldName);\r
1508   HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);\r
1509   if (HttpHeader->FieldName == NULL) {\r
1510     return EFI_OUT_OF_RESOURCES;\r
1511   }\r
1512   CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);\r
1513   HttpHeader->FieldName[FieldNameSize - 1] = 0;\r
1514 \r
1515   FieldValueSize = AsciiStrSize (FieldValue);\r
1516   HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);\r
1517   if (HttpHeader->FieldValue == NULL) {\r
1518     FreePool (HttpHeader->FieldName);\r
1519     return EFI_OUT_OF_RESOURCES;\r
1520   }\r
1521   CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);\r
1522   HttpHeader->FieldValue[FieldValueSize - 1] = 0;\r
1523 \r
1524   return EFI_SUCCESS;\r
1525 }\r
1526 \r
1527 /**\r
1528   Get one key/value header pair from the raw string.\r
1529 \r
1530   @param[in]  String             Pointer to the raw string.\r
1531   @param[out] FieldName          Points directly to field name within 'HttpHeader'.\r
1532   @param[out] FieldValue         Points directly to field value within 'HttpHeader'.\r
1533 \r
1534   @return     Pointer to the next raw string.\r
1535   @return     NULL if no key/value header pair from this raw string.\r
1536 \r
1537 **/\r
1538 CHAR8 *\r
1539 EFIAPI\r
1540 HttpGetFieldNameAndValue (\r
1541   IN     CHAR8   *String,\r
1542      OUT CHAR8   **FieldName,\r
1543      OUT CHAR8   **FieldValue\r
1544   )\r
1545 {\r
1546   CHAR8  *FieldNameStr;\r
1547   CHAR8  *FieldValueStr;\r
1548   CHAR8  *StrPtr;\r
1549   CHAR8  *EndofHeader;\r
1550 \r
1551   if (String == NULL || FieldName == NULL || FieldValue == NULL) {\r
1552     return NULL;\r
1553   }\r
1554 \r
1555   *FieldName    = NULL;\r
1556   *FieldValue   = NULL;\r
1557   FieldNameStr  = NULL;\r
1558   FieldValueStr = NULL;\r
1559   StrPtr        = NULL;\r
1560   EndofHeader   = NULL;\r
1561 \r
1562 \r
1563   //\r
1564   // Check whether the raw HTTP header string is valid or not.\r
1565   //\r
1566   EndofHeader = AsciiStrStr (String, "\r\n\r\n");\r
1567   if (EndofHeader == NULL) {\r
1568     return NULL;\r
1569   }\r
1570 \r
1571   //\r
1572   // Each header field consists of a name followed by a colon (":") and the field value.\r
1573   // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
1574   //\r
1575   // message-header = field-name ":" [ field-value ]\r
1576   // field-name = token\r
1577   // field-value = *( field-content | LWS )\r
1578   //\r
1579   // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.\r
1580   //       [element] means element is optional.\r
1581   //       LWS  = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.\r
1582   //       CRLF = '\r\n'.\r
1583   //       SP   = ' '.\r
1584   //       HT   = '\t' (Tab).\r
1585   //\r
1586   FieldNameStr = String;\r
1587   FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');\r
1588   if (FieldValueStr == NULL) {\r
1589     return NULL;\r
1590   }\r
1591 \r
1592   //\r
1593   // Replace ':' with 0, then FieldName has been retrived from String.\r
1594   //\r
1595   *(FieldValueStr - 1) = 0;\r
1596 \r
1597   //\r
1598   // Handle FieldValueStr, skip all the preceded LWS.\r
1599   //\r
1600   while (TRUE) {\r
1601     if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {\r
1602       //\r
1603       // Boundary condition check.\r
1604       //\r
1605       if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) {\r
1606         //\r
1607         // Wrong String format!\r
1608         //\r
1609         return NULL;\r
1610       }\r
1611 \r
1612       FieldValueStr ++;\r
1613     } else if (*FieldValueStr == '\r') {\r
1614       //\r
1615       // Boundary condition check.\r
1616       //\r
1617       if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) {\r
1618         //\r
1619         // No more preceded LWS, so break here.\r
1620         //\r
1621         break;\r
1622       }\r
1623 \r
1624       if (*(FieldValueStr + 1) == '\n' ) {\r
1625         if (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t') {\r
1626           FieldValueStr = FieldValueStr + 3;\r
1627         } else {\r
1628           //\r
1629           // No more preceded LWS, so break here.\r
1630           //\r
1631           break;\r
1632         }\r
1633       } else {\r
1634         //\r
1635         // Wrong String format!\r
1636         //\r
1637         return NULL;\r
1638       }\r
1639     } else {\r
1640       //\r
1641       // No more preceded LWS, so break here.\r
1642       //\r
1643       break;\r
1644     }\r
1645   }\r
1646 \r
1647   StrPtr = FieldValueStr;\r
1648   do {\r
1649     //\r
1650     // Handle the LWS within the field value.\r
1651     //\r
1652     StrPtr = AsciiStrGetNextToken (StrPtr, '\r');\r
1653     if (StrPtr == NULL || *StrPtr != '\n') {\r
1654       //\r
1655       // Wrong String format!\r
1656       //\r
1657       return NULL;\r
1658     }\r
1659 \r
1660     StrPtr++;\r
1661   } while (*StrPtr == ' ' || *StrPtr == '\t');\r
1662 \r
1663   //\r
1664   // Replace '\r' with 0\r
1665   //\r
1666   *(StrPtr - 2) = 0;\r
1667 \r
1668   //\r
1669   // Get FieldName and FieldValue.\r
1670   //\r
1671   *FieldName = FieldNameStr;\r
1672   *FieldValue = FieldValueStr;\r
1673 \r
1674   return StrPtr;\r
1675 }\r
1676 \r
1677 /**\r
1678   Free existing HeaderFields.\r
1679 \r
1680   @param[in]  HeaderFields       Pointer to array of key/value header pairs waitting for free.\r
1681   @param[in]  FieldCount         The number of header pairs in HeaderFields.\r
1682 \r
1683 **/\r
1684 VOID\r
1685 EFIAPI\r
1686 HttpFreeHeaderFields (\r
1687   IN  EFI_HTTP_HEADER  *HeaderFields,\r
1688   IN  UINTN            FieldCount\r
1689   )\r
1690 {\r
1691   UINTN                       Index;\r
1692 \r
1693   if (HeaderFields != NULL) {\r
1694     for (Index = 0; Index < FieldCount; Index++) {\r
1695       if (HeaderFields[Index].FieldName != NULL) {\r
1696         FreePool (HeaderFields[Index].FieldName);\r
1697       }\r
1698       if (HeaderFields[Index].FieldValue != NULL) {\r
1699         FreePool (HeaderFields[Index].FieldValue);\r
1700       }\r
1701     }\r
1702 \r
1703     FreePool (HeaderFields);\r
1704   }\r
1705 }\r
1706 \r
1707 /**\r
1708   Generate HTTP request message.\r
1709 \r
1710   This function will allocate memory for the whole HTTP message and generate a\r
1711   well formatted HTTP Request message in it, include the Request-Line, header\r
1712   fields and also the message body. It is the caller's responsibility to free\r
1713   the buffer returned in *RequestMsg.\r
1714 \r
1715   @param[in]   Message            Pointer to the EFI_HTTP_MESSAGE structure which\r
1716                                   contains the required information to generate\r
1717                                   the HTTP request message.\r
1718   @param[in]   Url                The URL of a remote host.\r
1719   @param[out]  RequestMsg         Pointer to the created HTTP request message.\r
1720                                   NULL if any error occured.\r
1721   @param[out]  RequestMsgSize     Size of the RequestMsg (in bytes).\r
1722 \r
1723   @retval EFI_SUCCESS             If HTTP request string was created successfully.\r
1724   @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.\r
1725   @retval EFI_INVALID_PARAMETER   The input arguments are invalid.\r
1726 \r
1727 **/\r
1728 EFI_STATUS\r
1729 EFIAPI\r
1730 HttpGenRequestMessage (\r
1731   IN     CONST EFI_HTTP_MESSAGE        *Message,\r
1732   IN     CONST CHAR8                   *Url,\r
1733      OUT CHAR8                         **RequestMsg,\r
1734      OUT UINTN                         *RequestMsgSize\r
1735   )\r
1736 {\r
1737   EFI_STATUS                       Status;\r
1738   UINTN                            StrLength;\r
1739   CHAR8                            *RequestPtr;\r
1740   UINTN                            HttpHdrSize;\r
1741   UINTN                            MsgSize;\r
1742   BOOLEAN                          Success;\r
1743   VOID                             *HttpHdr;\r
1744   EFI_HTTP_HEADER                  **AppendList;\r
1745   UINTN                            Index;\r
1746   EFI_HTTP_UTILITIES_PROTOCOL      *HttpUtilitiesProtocol;\r
1747 \r
1748   Status                = EFI_SUCCESS;\r
1749   HttpHdrSize           = 0;\r
1750   MsgSize               = 0;\r
1751   Success               = FALSE;\r
1752   HttpHdr               = NULL;\r
1753   AppendList            = NULL;\r
1754   HttpUtilitiesProtocol = NULL;\r
1755 \r
1756   //\r
1757   // 1. If we have a Request, we cannot have a NULL Url\r
1758   // 2. If we have a Request, HeaderCount can not be non-zero\r
1759   // 3. If we do not have a Request, HeaderCount should be zero\r
1760   // 4. If we do not have Request and Headers, we need at least a message-body\r
1761   //\r
1762   if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) ||\r
1763       (Message->Data.Request != NULL && Url == NULL) ||\r
1764       (Message->Data.Request != NULL && Message->HeaderCount == 0) ||\r
1765       (Message->Data.Request == NULL && Message->HeaderCount != 0) ||\r
1766       (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {\r
1767     return EFI_INVALID_PARAMETER;\r
1768   }\r
1769 \r
1770   if (Message->HeaderCount != 0) {\r
1771     //\r
1772     // Locate the HTTP_UTILITIES protocol.\r
1773     //\r
1774     Status = gBS->LocateProtocol (\r
1775                     &gEfiHttpUtilitiesProtocolGuid,\r
1776                     NULL,\r
1777                     (VOID **) &HttpUtilitiesProtocol\r
1778                     );\r
1779 \r
1780     if (EFI_ERROR (Status)) {\r
1781       DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));\r
1782       return Status;\r
1783     }\r
1784 \r
1785     //\r
1786     // Build AppendList to send into HttpUtilitiesBuild\r
1787     //\r
1788     AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));\r
1789     if (AppendList == NULL) {\r
1790       return EFI_OUT_OF_RESOURCES;\r
1791     }\r
1792 \r
1793     for(Index = 0; Index < Message->HeaderCount; Index++){\r
1794       AppendList[Index] = &Message->Headers[Index];\r
1795     }\r
1796 \r
1797     //\r
1798     // Build raw HTTP Headers\r
1799     //\r
1800     Status = HttpUtilitiesProtocol->Build (\r
1801                                       HttpUtilitiesProtocol,\r
1802                                       0,\r
1803                                       NULL,\r
1804                                       0,\r
1805                                       NULL,\r
1806                                       Message->HeaderCount,\r
1807                                       AppendList,\r
1808                                       &HttpHdrSize,\r
1809                                       &HttpHdr\r
1810                                       );\r
1811 \r
1812     FreePool (AppendList);\r
1813 \r
1814     if (EFI_ERROR (Status) || HttpHdr == NULL){\r
1815       return Status;\r
1816     }\r
1817   }\r
1818 \r
1819   //\r
1820   // If we have headers to be sent, account for it.\r
1821   //\r
1822   if (Message->HeaderCount != 0) {\r
1823     MsgSize = HttpHdrSize;\r
1824   }\r
1825 \r
1826   //\r
1827   // If we have a request line, account for the fields.\r
1828   //\r
1829   if (Message->Data.Request != NULL) {\r
1830     MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);\r
1831   }\r
1832 \r
1833 \r
1834   //\r
1835   // If we have a message body to be sent, account for it.\r
1836   //\r
1837   MsgSize += Message->BodyLength;\r
1838 \r
1839   //\r
1840   // memory for the string that needs to be sent to TCP\r
1841   //\r
1842   *RequestMsg = NULL;\r
1843   *RequestMsg = AllocateZeroPool (MsgSize);\r
1844   if (*RequestMsg == NULL) {\r
1845     Status = EFI_OUT_OF_RESOURCES;\r
1846     goto Exit;\r
1847   }\r
1848 \r
1849   RequestPtr = *RequestMsg;\r
1850   //\r
1851   // Construct header request\r
1852   //\r
1853   if (Message->Data.Request != NULL) {\r
1854     switch (Message->Data.Request->Method) {\r
1855     case HttpMethodGet:\r
1856       StrLength = sizeof (HTTP_METHOD_GET) - 1;\r
1857       CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);\r
1858       RequestPtr += StrLength;\r
1859       break;\r
1860     case HttpMethodPut:\r
1861       StrLength = sizeof (HTTP_METHOD_PUT) - 1;\r
1862       CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);\r
1863       RequestPtr += StrLength;\r
1864       break;\r
1865     case HttpMethodPatch:\r
1866       StrLength = sizeof (HTTP_METHOD_PATCH) - 1;\r
1867       CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);\r
1868       RequestPtr += StrLength;\r
1869       break;\r
1870     case HttpMethodPost:\r
1871       StrLength = sizeof (HTTP_METHOD_POST) - 1;\r
1872       CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);\r
1873       RequestPtr += StrLength;\r
1874       break;\r
1875     case HttpMethodHead:\r
1876       StrLength = sizeof (HTTP_METHOD_HEAD) - 1;\r
1877       CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);\r
1878       RequestPtr += StrLength;\r
1879       break;\r
1880     case HttpMethodDelete:\r
1881       StrLength = sizeof (HTTP_METHOD_DELETE) - 1;\r
1882       CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);\r
1883       RequestPtr += StrLength;\r
1884       break;\r
1885     default:\r
1886       ASSERT (FALSE);\r
1887       Status = EFI_INVALID_PARAMETER;\r
1888       goto Exit;\r
1889     }\r
1890 \r
1891     StrLength = AsciiStrLen(EMPTY_SPACE);\r
1892     CopyMem (RequestPtr, EMPTY_SPACE, StrLength);\r
1893     RequestPtr += StrLength;\r
1894 \r
1895     StrLength = AsciiStrLen (Url);\r
1896     CopyMem (RequestPtr, Url, StrLength);\r
1897     RequestPtr += StrLength;\r
1898 \r
1899     StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;\r
1900     CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);\r
1901     RequestPtr += StrLength;\r
1902 \r
1903     if (HttpHdr != NULL) {\r
1904       //\r
1905       // Construct header\r
1906       //\r
1907       CopyMem (RequestPtr, HttpHdr, HttpHdrSize);\r
1908       RequestPtr += HttpHdrSize;\r
1909     }\r
1910   }\r
1911 \r
1912   //\r
1913   // Construct body\r
1914   //\r
1915   if (Message->Body != NULL) {\r
1916     CopyMem (RequestPtr, Message->Body, Message->BodyLength);\r
1917     RequestPtr += Message->BodyLength;\r
1918   }\r
1919 \r
1920   //\r
1921   // Done\r
1922   //\r
1923   (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);\r
1924   Success     = TRUE;\r
1925 \r
1926 Exit:\r
1927 \r
1928   if (!Success) {\r
1929     if (*RequestMsg != NULL) {\r
1930       FreePool (*RequestMsg);\r
1931     }\r
1932     *RequestMsg = NULL;\r
1933     return Status;\r
1934   }\r
1935 \r
1936   if (HttpHdr != NULL) {\r
1937     FreePool (HttpHdr);\r
1938   }\r
1939 \r
1940   return EFI_SUCCESS;\r
1941 }\r
1942 \r
1943 /**\r
1944   Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined\r
1945   in UEFI 2.5 specification.\r
1946 \r
1947   @param[in]  StatusCode         The status code value in HTTP message.\r
1948 \r
1949   @return                        Value defined in EFI_HTTP_STATUS_CODE .\r
1950 \r
1951 **/\r
1952 EFI_HTTP_STATUS_CODE\r
1953 EFIAPI\r
1954 HttpMappingToStatusCode (\r
1955   IN UINTN                  StatusCode\r
1956   )\r
1957 {\r
1958   switch (StatusCode) {\r
1959   case 100:\r
1960     return HTTP_STATUS_100_CONTINUE;\r
1961   case 101:\r
1962     return HTTP_STATUS_101_SWITCHING_PROTOCOLS;\r
1963   case 200:\r
1964     return HTTP_STATUS_200_OK;\r
1965   case 201:\r
1966     return HTTP_STATUS_201_CREATED;\r
1967   case 202:\r
1968     return HTTP_STATUS_202_ACCEPTED;\r
1969   case 203:\r
1970     return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;\r
1971   case 204:\r
1972     return HTTP_STATUS_204_NO_CONTENT;\r
1973   case 205:\r
1974     return HTTP_STATUS_205_RESET_CONTENT;\r
1975   case 206:\r
1976     return HTTP_STATUS_206_PARTIAL_CONTENT;\r
1977   case 300:\r
1978     return HTTP_STATUS_300_MULTIPLE_CHOICES;\r
1979   case 301:\r
1980     return HTTP_STATUS_301_MOVED_PERMANENTLY;\r
1981   case 302:\r
1982     return HTTP_STATUS_302_FOUND;\r
1983   case 303:\r
1984     return HTTP_STATUS_303_SEE_OTHER;\r
1985   case 304:\r
1986     return HTTP_STATUS_304_NOT_MODIFIED;\r
1987   case 305:\r
1988     return HTTP_STATUS_305_USE_PROXY;\r
1989   case 307:\r
1990     return HTTP_STATUS_307_TEMPORARY_REDIRECT;\r
1991   case 308:\r
1992     return HTTP_STATUS_308_PERMANENT_REDIRECT;\r
1993   case 400:\r
1994     return HTTP_STATUS_400_BAD_REQUEST;\r
1995   case 401:\r
1996     return HTTP_STATUS_401_UNAUTHORIZED;\r
1997   case 402:\r
1998     return HTTP_STATUS_402_PAYMENT_REQUIRED;\r
1999   case 403:\r
2000     return HTTP_STATUS_403_FORBIDDEN;\r
2001   case 404:\r
2002     return HTTP_STATUS_404_NOT_FOUND;\r
2003   case 405:\r
2004     return HTTP_STATUS_405_METHOD_NOT_ALLOWED;\r
2005   case 406:\r
2006     return HTTP_STATUS_406_NOT_ACCEPTABLE;\r
2007   case 407:\r
2008     return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;\r
2009   case 408:\r
2010     return HTTP_STATUS_408_REQUEST_TIME_OUT;\r
2011   case 409:\r
2012     return HTTP_STATUS_409_CONFLICT;\r
2013   case 410:\r
2014     return HTTP_STATUS_410_GONE;\r
2015   case 411:\r
2016     return HTTP_STATUS_411_LENGTH_REQUIRED;\r
2017   case 412:\r
2018     return HTTP_STATUS_412_PRECONDITION_FAILED;\r
2019   case 413:\r
2020     return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;\r
2021   case 414:\r
2022     return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;\r
2023   case 415:\r
2024     return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;\r
2025   case 416:\r
2026     return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;\r
2027   case 417:\r
2028     return HTTP_STATUS_417_EXPECTATION_FAILED;\r
2029   case 500:\r
2030     return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;\r
2031   case 501:\r
2032     return HTTP_STATUS_501_NOT_IMPLEMENTED;\r
2033   case 502:\r
2034     return HTTP_STATUS_502_BAD_GATEWAY;\r
2035   case 503:\r
2036     return HTTP_STATUS_503_SERVICE_UNAVAILABLE;\r
2037   case 504:\r
2038     return HTTP_STATUS_504_GATEWAY_TIME_OUT;\r
2039   case 505:\r
2040     return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;\r
2041 \r
2042   default:\r
2043     return HTTP_STATUS_UNSUPPORTED_STATUS;\r
2044   }\r
2045 }\r
2046 \r
2047 /**\r
2048   Check whether header field called FieldName is in DeleteList.\r
2049 \r
2050   @param[in]  DeleteList        Pointer to array of key/value header pairs.\r
2051   @param[in]  DeleteCount       The number of header pairs.\r
2052   @param[in]  FieldName         Pointer to header field's name.\r
2053 \r
2054   @return     TRUE if FieldName is not in DeleteList, that means this header field is valid.\r
2055   @return     FALSE if FieldName is in DeleteList, that means this header field is invalid.\r
2056 \r
2057 **/\r
2058 BOOLEAN\r
2059 EFIAPI\r
2060 HttpIsValidHttpHeader (\r
2061   IN  CHAR8            *DeleteList[],\r
2062   IN  UINTN            DeleteCount,\r
2063   IN  CHAR8            *FieldName\r
2064   )\r
2065 {\r
2066   UINTN                       Index;\r
2067 \r
2068   if (FieldName == NULL) {\r
2069     return FALSE;\r
2070   }\r
2071 \r
2072   for (Index = 0; Index < DeleteCount; Index++) {\r
2073     if (DeleteList[Index] == NULL) {\r
2074       continue;\r
2075     }\r
2076 \r
2077     if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {\r
2078       return FALSE;\r
2079     }\r
2080   }\r
2081 \r
2082   return TRUE;\r
2083 }\r
2084 \r