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