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