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