]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootClient.c
NetworkPkg: Add UEFI HTTP boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
1 /** @file
2 Implementation of the boot file download function.
3
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "HttpBootDxe.h"
16
17 /**
18 Update the IP and URL device path node to include the boot resource information.
19
20 @param[in] Private The pointer to the driver's private data.
21
22 @retval EFI_SUCCESS Device patch successfully updated.
23 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
24 @retval Others Unexpected error happened.
25
26 **/
27 EFI_STATUS
28 HttpBootUpdateDevicePath (
29 IN HTTP_BOOT_PRIVATE_DATA *Private
30 )
31 {
32 EFI_DEV_PATH *Node;
33 EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
34 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
35 UINTN Length;
36 EFI_STATUS Status;
37
38 TmpDevicePath = NULL;
39
40 //
41 // Update the IP node with DHCP assigned information.
42 //
43 if (!Private->UsingIpv6) {
44 Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
45 if (Node == NULL) {
46 return EFI_OUT_OF_RESOURCES;
47 }
48 Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
49 Node->Ipv4.Header.SubType = MSG_IPv4_DP;
50 SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
51 CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
52 Node->Ipv4.RemotePort = Private->Port;
53 Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;
54 Node->Ipv4.StaticIpAddress = FALSE;
55 CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
56 CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
57
58 TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
59 FreePool (Node);
60 if (TmpDevicePath == NULL) {
61 return EFI_OUT_OF_RESOURCES;
62 }
63 } else {
64 ASSERT (FALSE);
65 }
66
67 //
68 // Update the URI node with the boot file URI.
69 //
70 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
71 Node = AllocatePool (Length);
72 if (Node == NULL) {
73 FreePool (TmpDevicePath);
74 return EFI_OUT_OF_RESOURCES;
75 }
76 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
77 Node->DevPath.SubType = MSG_URI_DP;
78 SetDevicePathNodeLength (Node, Length);
79 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
80
81 NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
82 FreePool (Node);
83 FreePool (TmpDevicePath);
84 if (NewDevicePath == NULL) {
85 return EFI_OUT_OF_RESOURCES;
86 }
87
88 //
89 // Reinstall the device path protocol of the child handle.
90 //
91 Status = gBS->ReinstallProtocolInterface (
92 Private->ChildHandle,
93 &gEfiDevicePathProtocolGuid,
94 Private->DevicePath,
95 NewDevicePath
96 );
97 if (EFI_ERROR (Status)) {
98 return Status;
99 }
100
101 FreePool (Private->DevicePath);
102 Private->DevicePath = NewDevicePath;
103 return EFI_SUCCESS;
104 }
105
106 /**
107 Parse the boot file URI information from the selected Dhcp4 offer packet.
108
109 @param[in] Private The pointer to the driver's private data.
110
111 @retval EFI_SUCCESS Successfully parsed out all the boot information.
112 @retval Others Failed to parse out the boot information.
113
114 **/
115 EFI_STATUS
116 HttpBootExtractUriInfo (
117 IN HTTP_BOOT_PRIVATE_DATA *Private
118 )
119 {
120 HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;
121 HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;
122 UINT32 SelectIndex;
123 UINT32 ProxyIndex;
124 EFI_DHCP4_PACKET_OPTION *Option;
125 EFI_STATUS Status;
126
127 ASSERT (Private != NULL);
128 ASSERT (Private->SelectIndex != 0);
129 SelectIndex = Private->SelectIndex - 1;
130 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
131
132 Status = EFI_SUCCESS;
133
134 //
135 // SelectOffer contains the IP address configuration and name server configuration.
136 // HttpOffer contains the boot file URL.
137 //
138 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
139 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
140 HttpOffer = SelectOffer;
141 } else {
142 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
143 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
144 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
145 }
146
147 //
148 // Configure the default DNS server if server assigned.
149 //
150 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {
151 Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
152 ASSERT (Option != NULL);
153 Status = HttpBootRegisterIp4Dns (
154 Private,
155 Option->Length,
156 Option->Data
157 );
158 if (EFI_ERROR (Status)) {
159 return Status;
160 }
161 }
162
163 //
164 // Extract the port from URL, and use default HTTP port 80 if not provided.
165 //
166 Status = HttpUrlGetPort (
167 (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
168 HttpOffer->UriParser,
169 &Private->Port
170 );
171 if (EFI_ERROR (Status) || Private->Port == 0) {
172 Private->Port = 80;
173 }
174
175 //
176 // Record the URI of boot file from the selected HTTP offer.
177 //
178 Private->BootFileUriParser = HttpOffer->UriParser;
179 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
180
181
182 //
183 // All boot informations are valid here.
184 //
185 AsciiPrint ("\n URI: %a", Private->BootFileUri);
186
187 //
188 // Update the device path to include the IP and boot URI information.
189 //
190 Status = HttpBootUpdateDevicePath (Private);
191
192 return Status;
193 }
194
195 /**
196 Discover all the boot information for boot file.
197
198 @param[in, out] Private The pointer to the driver's private data.
199
200 @retval EFI_SUCCESS Successfully obtained all the boot information .
201 @retval Others Failed to retrieve the boot information.
202
203 **/
204 EFI_STATUS
205 HttpBootDiscoverBootInfo (
206 IN OUT HTTP_BOOT_PRIVATE_DATA *Private
207 )
208 {
209 EFI_STATUS Status;
210
211 //
212 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
213 // other Http boot information.
214 //
215 Status = HttpBootDhcp (Private);
216 if (EFI_ERROR (Status)) {
217 return Status;
218 }
219
220 if (!Private->UsingIpv6) {
221 Status = HttpBootExtractUriInfo (Private);
222 } else {
223 ASSERT (FALSE);
224 }
225
226 return Status;
227 }
228
229 /**
230 Create a HttpIo instance for the file download.
231
232 @param[in] Private The pointer to the driver's private data.
233
234 @retval EFI_SUCCESS Successfully created.
235 @retval Others Failed to create HttpIo.
236
237 **/
238 EFI_STATUS
239 HttpBootCreateHttpIo (
240 IN HTTP_BOOT_PRIVATE_DATA *Private
241 )
242 {
243 HTTP_IO_CONFIG_DATA ConfigData;
244 EFI_STATUS Status;
245
246 ASSERT (Private != NULL);
247
248 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
249 if (!Private->UsingIpv6) {
250 ConfigData.Config4.HttpVersion = HttpVersion11;
251 ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
252 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
253 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
254 } else {
255 ASSERT (FALSE);
256 }
257
258 Status = HttpIoCreateIo (
259 Private->Image,
260 Private->Controller,
261 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
262 &ConfigData,
263 &Private->HttpIo
264 );
265 if (EFI_ERROR (Status)) {
266 return Status;
267 }
268
269 Private->HttpCreated = TRUE;
270 return EFI_SUCCESS;
271 }
272
273 /**
274 Get the file content from cached data.
275
276 @param[in] Private The pointer to the driver's private data.
277 @param[in] Uri Uri of the file to be retrieved from cache.
278 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
279 code of EFI_SUCCESS, the amount of data transferred to
280 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
281 the size of Buffer required to retrieve the requested file.
282 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
283 then the size of the requested file is returned in
284 BufferSize.
285
286 @retval EFI_SUCCESS Successfully created.
287 @retval Others Failed to create HttpIo.
288
289 **/
290 EFI_STATUS
291 HttpBootGetFileFromCache (
292 IN HTTP_BOOT_PRIVATE_DATA *Private,
293 IN CHAR16 *Uri,
294 IN OUT UINTN *BufferSize,
295 OUT UINT8 *Buffer
296 )
297 {
298 LIST_ENTRY *Entry;
299 LIST_ENTRY *Entry2;
300 HTTP_BOOT_CACHE_CONTENT *Cache;
301 HTTP_BOOT_ENTITY_DATA *EntityData;
302 UINTN CopyedSize;
303
304 if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {
305 return EFI_INVALID_PARAMETER;
306 }
307
308 NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
309 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
310 //
311 // Compare the URI to see whether we already have a cache for this file.
312 //
313 if ((Cache->RequestData != NULL) &&
314 (Cache->RequestData->Url != NULL) &&
315 (StrCmp (Uri, Cache->RequestData->Url) == 0))
316 {
317 //
318 // Hit cache, check buffer size.
319 //
320 if (*BufferSize < Cache->EntityLength) {
321 *BufferSize = Cache->EntityLength;
322 return EFI_BUFFER_TOO_SMALL;
323 }
324
325 //
326 // Fill data to buffer.
327 //
328 CopyedSize = 0;
329 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
330 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
331 if (*BufferSize > CopyedSize) {
332 CopyMem (
333 Buffer + CopyedSize,
334 EntityData->DataStart,
335 MIN (EntityData->DataLength, *BufferSize - CopyedSize)
336 );
337 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
338 }
339 }
340 *BufferSize = CopyedSize;
341 return EFI_SUCCESS;
342 }
343 }
344
345 return EFI_NOT_FOUND;
346 }
347
348 /**
349 Release all the resource of a cache item.
350
351 @param[in] Cache The pointer to the cache item.
352
353 **/
354 VOID
355 HttpBootFreeCache (
356 IN HTTP_BOOT_CACHE_CONTENT *Cache
357 )
358 {
359 UINTN Index;
360 LIST_ENTRY *Entry;
361 LIST_ENTRY *NextEntry;
362 HTTP_BOOT_ENTITY_DATA *EntityData;
363
364 if (Cache != NULL) {
365 //
366 // Free the request data
367 //
368 if (Cache->RequestData != NULL) {
369 if (Cache->RequestData->Url != NULL) {
370 FreePool (Cache->RequestData->Url);
371 }
372 FreePool (Cache->RequestData);
373 }
374
375 //
376 // Free the response header
377 //
378 if (Cache->ResponseData != NULL) {
379 if (Cache->ResponseData->Headers != NULL) {
380 for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
381 FreePool (Cache->ResponseData->Headers[Index].FieldName);
382 FreePool (Cache->ResponseData->Headers[Index].FieldValue);
383 }
384 FreePool (Cache->ResponseData->Headers);
385 }
386 }
387
388 //
389 // Free the response body
390 //
391 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
392 EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
393 if (EntityData->Block != NULL) {
394 FreePool (EntityData->Block);
395 }
396 RemoveEntryList (&EntityData->Link);
397 FreePool (EntityData);
398 }
399
400 FreePool (Cache);
401 }
402 }
403
404 /**
405 Clean up all cached data.
406
407 @param[in] Private The pointer to the driver's private data.
408
409 **/
410 VOID
411 HttpBootFreeCacheList (
412 IN HTTP_BOOT_PRIVATE_DATA *Private
413 )
414 {
415 LIST_ENTRY *Entry;
416 LIST_ENTRY *NextEntry;
417 HTTP_BOOT_CACHE_CONTENT *Cache;
418
419 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
420 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
421 RemoveEntryList (&Cache->Link);
422 HttpBootFreeCache (Cache);
423 }
424 }
425
426 /**
427 A callback function to intercept events during message parser.
428
429 This function will be invoked during HttpParseMessageBody() with various events type. An error
430 return status of the callback function will cause the HttpParseMessageBody() aborted.
431
432 @param[in] EventType Event type of this callback call.
433 @param[in] Data A pointer to data buffer.
434 @param[in] Length Length in bytes of the Data.
435 @param[in] Context Callback context set by HttpInitMsgParser().
436
437 @retval EFI_SUCCESS Continue to parser the message body.
438 @retval Others Abort the parse.
439
440 **/
441 EFI_STATUS
442 EFIAPI
443 HttpBootGetBootFileCallback (
444 IN HTTP_BODY_PARSE_EVENT EventType,
445 IN CHAR8 *Data,
446 IN UINTN Length,
447 IN VOID *Context
448 )
449 {
450 HTTP_BOOT_CALLBACK_DATA *CallbackData;
451 HTTP_BOOT_ENTITY_DATA *NewEntityData;
452
453 //
454 // We only care about the entity data.
455 //
456 if (EventType != BodyParseEventOnData) {
457 return EFI_SUCCESS;
458 }
459
460 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
461
462 //
463 // Save the data into cache list.
464 //
465 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
466 if (NewEntityData == NULL) {
467 return EFI_OUT_OF_RESOURCES;
468 }
469 if (CallbackData->NewBlock) {
470 NewEntityData->Block = CallbackData->Block;
471 CallbackData->Block = NULL;
472 }
473 NewEntityData->DataLength = Length;
474 NewEntityData->DataStart = (UINT8*) Data;
475 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
476
477 //
478 // Copy data if caller has provided a buffer.
479 //
480 if (CallbackData->BufferSize > CallbackData->CopyedSize) {
481 CopyMem (
482 CallbackData->Buffer + CallbackData->CopyedSize,
483 Data,
484 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
485 );
486 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
487 }
488
489 return EFI_SUCCESS;
490 }
491
492 /**
493 This function download the boot file by using UEFI HTTP protocol.
494
495 @param[in] Private The pointer to the driver's private data.
496 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
497 the caller only want to know the size of the requested file.
498 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
499 code of EFI_SUCCESS, the amount of data transferred to
500 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
501 the size of Buffer required to retrieve the requested file.
502 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
503 then the size of the requested file is returned in
504 BufferSize.
505
506 @retval EFI_SUCCESS The file was loaded.
507 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
508 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
509 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
510 BufferSize has been updated with the size needed to complete
511 the request.
512 @retval Others Unexpected error happened.
513
514 **/
515 EFI_STATUS
516 HttpBootGetBootFile (
517 IN HTTP_BOOT_PRIVATE_DATA *Private,
518 IN BOOLEAN HeaderOnly,
519 IN OUT UINTN *BufferSize,
520 OUT UINT8 *Buffer
521 )
522 {
523 EFI_STATUS Status;
524 CHAR8 *HostName;
525 EFI_HTTP_REQUEST_DATA *RequestData;
526 HTTP_IO_RESOPNSE_DATA *ResponseData;
527 HTTP_IO_RESOPNSE_DATA ResponseBody;
528 HTTP_IO *HttpIo;
529 HTTP_IO_HEADER *HttpIoHeader;
530 VOID *Parser;
531 HTTP_BOOT_CALLBACK_DATA Context;
532 UINTN ContentLength;
533 HTTP_BOOT_CACHE_CONTENT *Cache;
534 UINT8 *Block;
535 CHAR16 *Url;
536
537 ASSERT (Private != NULL);
538 ASSERT (Private->HttpCreated);
539
540 if (BufferSize == NULL) {
541 return EFI_INVALID_PARAMETER;
542 }
543
544 if (*BufferSize != 0 && Buffer == NULL) {
545 return EFI_INVALID_PARAMETER;
546 }
547
548 //
549 // First, check whether we already cached the requested Uri.
550 //
551 Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));
552 if (Url == NULL) {
553 return EFI_OUT_OF_RESOURCES;
554 }
555 AsciiStrToUnicodeStr (Private->BootFileUri, Url);
556 if (!HeaderOnly) {
557 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);
558 if (Status != EFI_NOT_FOUND) {
559 FreePool (Url);
560 return Status;
561 }
562 }
563
564 //
565 // Not found in cache, try to download it through HTTP.
566 //
567
568 //
569 // 1. Create a temp cache item for the requested URI.
570 //
571 Cache = NULL;
572 if (!HeaderOnly) {
573 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
574 if (Cache == NULL) {
575 Status = EFI_OUT_OF_RESOURCES;
576 goto ERROR_1;
577 }
578 InitializeListHead (&Cache->EntityDataList);
579 }
580
581 //
582 // 2. Send HTTP request message.
583 //
584
585 //
586 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
587 // Host
588 // Accept
589 // User-Agent
590 //
591 HttpIoHeader = HttpBootCreateHeader (3);
592 if (HttpIoHeader == NULL) {
593 Status = EFI_OUT_OF_RESOURCES;
594 goto ERROR_2;
595 }
596
597 //
598 // Add HTTP header field 1: Host
599 //
600 HostName = NULL;
601 Status = HttpUrlGetHostName (
602 Private->BootFileUri,
603 Private->BootFileUriParser,
604 &HostName
605 );
606 if (EFI_ERROR (Status)) {
607 goto ERROR_3;
608 }
609 Status = HttpBootSetHeader (
610 HttpIoHeader,
611 HTTP_FIELD_NAME_HOST,
612 HostName
613 );
614 FreePool (HostName);
615 if (EFI_ERROR (Status)) {
616 goto ERROR_3;
617 }
618
619 //
620 // Add HTTP header field 2: Accept
621 //
622 Status = HttpBootSetHeader (
623 HttpIoHeader,
624 HTTP_FIELD_NAME_ACCEPT,
625 "*/*"
626 );
627 if (EFI_ERROR (Status)) {
628 goto ERROR_3;
629 }
630
631 //
632 // Add HTTP header field 3: User-Agent
633 //
634 Status = HttpBootSetHeader (
635 HttpIoHeader,
636 HTTP_FIELD_NAME_USER_AGENT,
637 HTTP_USER_AGENT_EFI_HTTP_BOOT
638 );
639 if (EFI_ERROR (Status)) {
640 goto ERROR_3;
641 }
642
643 //
644 // 2.2 Build the rest of HTTP request info.
645 //
646 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
647 if (RequestData == NULL) {
648 Status = EFI_OUT_OF_RESOURCES;
649 goto ERROR_3;
650 }
651 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
652 RequestData->Url = Url;
653 if (RequestData->Url == NULL) {
654 Status = EFI_OUT_OF_RESOURCES;
655 goto ERROR_4;
656 }
657 AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);
658
659 //
660 // 2.3 Record the request info in a temp cache item.
661 //
662 if (!HeaderOnly) {
663 Cache->RequestData = RequestData;
664 }
665
666 //
667 // 2.4 Send out the request to HTTP server.
668 //
669 HttpIo = &Private->HttpIo;
670 Status = HttpIoSendRequest (
671 HttpIo,
672 RequestData,
673 HttpIoHeader->HeaderCount,
674 HttpIoHeader->Headers,
675 0,
676 NULL
677 );
678 if (EFI_ERROR (Status)) {
679 goto ERROR_4;
680 }
681
682 //
683 // 3. Receive HTTP response message.
684 //
685
686 //
687 // 3.1 First step, use zero BodyLength to only receive the response headers.
688 //
689 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));
690 if (ResponseData == NULL) {
691 Status = EFI_OUT_OF_RESOURCES;
692 goto ERROR_4;
693 }
694 Status = HttpIoRecvResponse (
695 &Private->HttpIo,
696 TRUE,
697 ResponseData
698 );
699 if (EFI_ERROR (Status)) {
700 goto ERROR_5;
701 }
702
703 //
704 // 3.2 Cache the response header.
705 //
706 if (!HeaderOnly) {
707 Cache->ResponseData = ResponseData;
708 }
709
710 //
711 // 3.3 Init a message-body parser from the header information.
712 //
713 Parser = NULL;
714 Context.NewBlock = FALSE;
715 Context.Block = NULL;
716 Context.CopyedSize = 0;
717 Context.Buffer = Buffer;
718 Context.BufferSize = *BufferSize;
719 Context.Cache = Cache;
720 Status = HttpInitMsgParser (
721 HeaderOnly? HttpMethodHead : HttpMethodGet,
722 ResponseData->Response.StatusCode,
723 ResponseData->HeaderCount,
724 ResponseData->Headers,
725 HttpBootGetBootFileCallback,
726 (VOID*) &Context,
727 &Parser
728 );
729 if (EFI_ERROR (Status)) {
730 goto ERROR_6;
731 }
732
733 //
734 // 3.4 Continue to receive and parse message-body if needed.
735 //
736 if (!HeaderOnly) {
737 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));
738 while (!HttpIsMessageComplete (Parser)) {
739 //
740 // Allocate a new block to hold the message-body.
741 //
742 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
743 if (Block == NULL) {
744 Status = EFI_OUT_OF_RESOURCES;
745 goto ERROR_6;
746 }
747 ResponseBody.Body = (CHAR8*) Block;
748 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
749 Status = HttpIoRecvResponse (
750 &Private->HttpIo,
751 FALSE,
752 &ResponseBody
753 );
754 if (EFI_ERROR (Status)) {
755 goto ERROR_6;
756 }
757
758 //
759 // Parse the new received block of the message-body, the block will be saved in cache.
760 //
761 Context.NewBlock = TRUE;
762 Context.Block = Block;
763 Status = HttpParseMessageBody (
764 Parser,
765 ResponseBody.BodyLength,
766 ResponseBody.Body
767 );
768 if (EFI_ERROR (Status)) {
769 goto ERROR_6;
770 }
771 }
772 }
773
774 //
775 // 3.5 Message-body receive & parse is completed, get the file size.
776 //
777 Status = HttpGetEntityLength (Parser, &ContentLength);
778 if (EFI_ERROR (Status)) {
779 goto ERROR_6;
780 }
781
782 if (*BufferSize < ContentLength) {
783 Status = EFI_BUFFER_TOO_SMALL;
784 }
785 *BufferSize = ContentLength;
786
787 //
788 // 4. Save the cache item to driver's cache list and return.
789 //
790 if (!HeaderOnly) {
791 Cache->EntityLength = ContentLength;
792 InsertTailList (&Private->CacheList, &Cache->Link);
793 }
794
795 if (Parser != NULL) {
796 HttpFreeMsgParser (Parser);
797 }
798
799 return EFI_SUCCESS;
800
801 ERROR_6:
802 if (Parser != NULL) {
803 HttpFreeMsgParser (Parser);
804 }
805 if (Context.Block != NULL) {
806 FreePool (Context.Block);
807 }
808 HttpBootFreeCache (Cache);
809
810 ERROR_5:
811 if (ResponseData != NULL) {
812 FreePool (ResponseData);
813 }
814 ERROR_4:
815 if (RequestData != NULL) {
816 FreePool (RequestData);
817 }
818 ERROR_3:
819 HttpBootFreeHeader (HttpIoHeader);
820 ERROR_2:
821 if (Cache != NULL) {
822 FreePool (Cache);
823 }
824 ERROR_1:
825 if (Url != NULL) {
826 FreePool (Url);
827 }
828
829 return Status;
830 }