5669c5f37ce1f511879fe117d34f8420cf2a12d3
[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 // Copy data if caller has provided a buffer.
463 //
464 if (CallbackData->BufferSize > CallbackData->CopyedSize) {
465 CopyMem (
466 CallbackData->Buffer + CallbackData->CopyedSize,
467 Data,
468 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
469 );
470 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
471 }
472
473 //
474 // The caller doesn't provide a buffer, save the block into cache list.
475 //
476 if (CallbackData->Cache != NULL) {
477 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
478 if (NewEntityData == NULL) {
479 return EFI_OUT_OF_RESOURCES;
480 }
481 if (CallbackData->NewBlock) {
482 NewEntityData->Block = CallbackData->Block;
483 CallbackData->Block = NULL;
484 }
485 NewEntityData->DataLength = Length;
486 NewEntityData->DataStart = (UINT8*) Data;
487 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
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 if caller doesn't provide buffer.
570 //
571 Cache = NULL;
572 if ((!HeaderOnly) && (*BufferSize == 0)) {
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 (Cache != NULL) {
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 (Cache != NULL) {
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 Block = NULL;
737 if (!HeaderOnly) {
738 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));
739 while (!HttpIsMessageComplete (Parser)) {
740 //
741 // Allocate a block to hold the message-body, if caller doesn't provide
742 // a buffer, the block will be cached and we will allocate a new one here.
743 //
744 if (Block == NULL || Context.BufferSize == 0) {
745 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
746 if (Block == NULL) {
747 Status = EFI_OUT_OF_RESOURCES;
748 goto ERROR_6;
749 }
750 Context.NewBlock = TRUE;
751 Context.Block = Block;
752 } else {
753 Context.NewBlock = FALSE;
754 }
755
756 ResponseBody.Body = (CHAR8*) Block;
757 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
758 Status = HttpIoRecvResponse (
759 &Private->HttpIo,
760 FALSE,
761 &ResponseBody
762 );
763 if (EFI_ERROR (Status)) {
764 goto ERROR_6;
765 }
766
767 //
768 // Parse the new received block of the message-body, the block will be saved in cache.
769 //
770 Status = HttpParseMessageBody (
771 Parser,
772 ResponseBody.BodyLength,
773 ResponseBody.Body
774 );
775 if (EFI_ERROR (Status)) {
776 goto ERROR_6;
777 }
778 }
779 }
780
781 //
782 // 3.5 Message-body receive & parse is completed, get the file size.
783 //
784 Status = HttpGetEntityLength (Parser, &ContentLength);
785 if (EFI_ERROR (Status)) {
786 goto ERROR_6;
787 }
788
789 if (*BufferSize < ContentLength) {
790 Status = EFI_BUFFER_TOO_SMALL;
791 }
792 *BufferSize = ContentLength;
793
794 //
795 // 4. Save the cache item to driver's cache list and return.
796 //
797 if (Cache != NULL) {
798 Cache->EntityLength = ContentLength;
799 InsertTailList (&Private->CacheList, &Cache->Link);
800 }
801
802 if (Parser != NULL) {
803 HttpFreeMsgParser (Parser);
804 }
805
806 return EFI_SUCCESS;
807
808 ERROR_6:
809 if (Parser != NULL) {
810 HttpFreeMsgParser (Parser);
811 }
812 if (Context.Block != NULL) {
813 FreePool (Context.Block);
814 }
815 HttpBootFreeCache (Cache);
816
817 ERROR_5:
818 if (ResponseData != NULL) {
819 FreePool (ResponseData);
820 }
821 ERROR_4:
822 if (RequestData != NULL) {
823 FreePool (RequestData);
824 }
825 ERROR_3:
826 HttpBootFreeHeader (HttpIoHeader);
827 ERROR_2:
828 if (Cache != NULL) {
829 FreePool (Cache);
830 }
831 ERROR_1:
832 if (Url != NULL) {
833 FreePool (Url);
834 }
835
836 return Status;
837 }