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