VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c

最後變更 在這個檔案是 99404,由 vboxsync 提交於 20 月 前

Devices/EFI/FirmwareNew: Update to edk2-stable202302 and make it build, bugref:4643

  • 屬性 svn:eol-style 設為 native
檔案大小: 26.1 KB
 
1/** @file
2 Http IO Helper Library.
3
4 (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6**/
7
8#include <Uefi.h>
9
10#include <Protocol/Http.h>
11
12#include <Library/BaseLib.h>
13#include <Library/BaseMemoryLib.h>
14#include <Library/DebugLib.h>
15#include <Library/HttpIoLib.h>
16#include <Library/MemoryAllocationLib.h>
17#include <Library/PrintLib.h>
18#include <Library/UefiBootServicesTableLib.h>
19
20/**
21 Notify the callback function when an event is triggered.
22
23 @param[in] Context The opaque parameter to the function.
24
25**/
26VOID
27EFIAPI
28HttpIoNotifyDpc (
29 IN VOID *Context
30 )
31{
32 *((BOOLEAN *)Context) = TRUE;
33}
34
35/**
36 Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
37
38 @param[in] Event The event signaled.
39 @param[in] Context The opaque parameter to the function.
40
41**/
42VOID
43EFIAPI
44HttpIoNotify (
45 IN EFI_EVENT Event,
46 IN VOID *Context
47 )
48{
49 //
50 // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
51 //
52 QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);
53}
54
55/**
56 Destroy the HTTP_IO and release the resources.
57
58 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
59
60**/
61VOID
62HttpIoDestroyIo (
63 IN HTTP_IO *HttpIo
64 )
65{
66 EFI_HTTP_PROTOCOL *Http;
67 EFI_EVENT Event;
68
69 if (HttpIo == NULL) {
70 return;
71 }
72
73 Event = HttpIo->ReqToken.Event;
74 if (Event != NULL) {
75 gBS->CloseEvent (Event);
76 }
77
78 Event = HttpIo->RspToken.Event;
79 if (Event != NULL) {
80 gBS->CloseEvent (Event);
81 }
82
83 Event = HttpIo->TimeoutEvent;
84 if (Event != NULL) {
85 gBS->CloseEvent (Event);
86 }
87
88 Http = HttpIo->Http;
89 if (Http != NULL) {
90 Http->Configure (Http, NULL);
91 gBS->CloseProtocol (
92 HttpIo->Handle,
93 &gEfiHttpProtocolGuid,
94 HttpIo->Image,
95 HttpIo->Controller
96 );
97 }
98
99 NetLibDestroyServiceChild (
100 HttpIo->Controller,
101 HttpIo->Image,
102 &gEfiHttpServiceBindingProtocolGuid,
103 HttpIo->Handle
104 );
105}
106
107/**
108 Create a HTTP_IO to access the HTTP service. It will create and configure
109 a HTTP child handle.
110
111 @param[in] Image The handle of the driver image.
112 @param[in] Controller The handle of the controller.
113 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
114 @param[in] ConfigData The HTTP_IO configuration data ,
115 NULL means not to configure the HTTP child.
116 @param[in] Callback Callback function which will be invoked when specified
117 HTTP_IO_CALLBACK_EVENT happened.
118 @param[in] Context The Context data which will be passed to the Callback function.
119 @param[out] HttpIo The HTTP_IO.
120
121 @retval EFI_SUCCESS The HTTP_IO is created and configured.
122 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
123 @retval EFI_UNSUPPORTED One or more of the control options are not
124 supported in the implementation.
125 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
126 @retval Others Failed to create the HTTP_IO or configure it.
127
128**/
129EFI_STATUS
130HttpIoCreateIo (
131 IN EFI_HANDLE Image,
132 IN EFI_HANDLE Controller,
133 IN UINT8 IpVersion,
134 IN HTTP_IO_CONFIG_DATA *ConfigData OPTIONAL,
135 IN HTTP_IO_CALLBACK Callback,
136 IN VOID *Context,
137 OUT HTTP_IO *HttpIo
138 )
139{
140 EFI_STATUS Status;
141 EFI_HTTP_CONFIG_DATA HttpConfigData;
142 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
143 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
144 EFI_HTTP_PROTOCOL *Http;
145 EFI_EVENT Event;
146
147 if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) {
148 return EFI_INVALID_PARAMETER;
149 }
150
151 if ((IpVersion != IP_VERSION_4) && (IpVersion != IP_VERSION_6)) {
152 return EFI_UNSUPPORTED;
153 }
154
155 ZeroMem (HttpIo, sizeof (HTTP_IO));
156 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
157
158 //
159 // Create the HTTP child instance and get the HTTP protocol.
160 //
161 Status = NetLibCreateServiceChild (
162 Controller,
163 Image,
164 &gEfiHttpServiceBindingProtocolGuid,
165 &HttpIo->Handle
166 );
167 if (EFI_ERROR (Status)) {
168 return Status;
169 }
170
171 Status = gBS->OpenProtocol (
172 HttpIo->Handle,
173 &gEfiHttpProtocolGuid,
174 (VOID **)&Http,
175 Image,
176 Controller,
177 EFI_OPEN_PROTOCOL_BY_DRIVER
178 );
179 if (EFI_ERROR (Status) || (Http == NULL)) {
180 goto ON_ERROR;
181 }
182
183 //
184 // Init the configuration data and configure the HTTP child.
185 //
186 HttpIo->Image = Image;
187 HttpIo->Controller = Controller;
188 HttpIo->IpVersion = IpVersion;
189 HttpIo->Http = Http;
190 HttpIo->Callback = Callback;
191 HttpIo->Context = Context;
192 HttpIo->Timeout = PcdGet32 (PcdHttpIoTimeout);
193
194 if (ConfigData != NULL) {
195 if (HttpIo->IpVersion == IP_VERSION_4) {
196 HttpConfigData.LocalAddressIsIPv6 = FALSE;
197 HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion;
198 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
199
200 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
201 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
202 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
203 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
204 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
205 } else {
206 HttpConfigData.LocalAddressIsIPv6 = TRUE;
207 HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion;
208 HttpConfigData.TimeOutMillisec = ConfigData->Config6.RequestTimeOut;
209
210 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
211 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
212 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
213 }
214
215 Status = Http->Configure (Http, &HttpConfigData);
216 if (EFI_ERROR (Status)) {
217 goto ON_ERROR;
218 }
219 }
220
221 //
222 // Create events for variuos asynchronous operations.
223 //
224 Status = gBS->CreateEvent (
225 EVT_NOTIFY_SIGNAL,
226 TPL_NOTIFY,
227 HttpIoNotify,
228 &HttpIo->IsTxDone,
229 &Event
230 );
231 if (EFI_ERROR (Status)) {
232 goto ON_ERROR;
233 }
234
235 HttpIo->ReqToken.Event = Event;
236 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
237
238 Status = gBS->CreateEvent (
239 EVT_NOTIFY_SIGNAL,
240 TPL_NOTIFY,
241 HttpIoNotify,
242 &HttpIo->IsRxDone,
243 &Event
244 );
245 if (EFI_ERROR (Status)) {
246 goto ON_ERROR;
247 }
248
249 HttpIo->RspToken.Event = Event;
250 HttpIo->RspToken.Message = &HttpIo->RspMessage;
251
252 //
253 // Create TimeoutEvent for response
254 //
255 Status = gBS->CreateEvent (
256 EVT_TIMER,
257 TPL_CALLBACK,
258 NULL,
259 NULL,
260 &Event
261 );
262 if (EFI_ERROR (Status)) {
263 goto ON_ERROR;
264 }
265
266 HttpIo->TimeoutEvent = Event;
267 return EFI_SUCCESS;
268
269ON_ERROR:
270 HttpIoDestroyIo (HttpIo);
271
272 return Status;
273}
274
275/**
276 Synchronously send a HTTP REQUEST message to the server.
277
278 @param[in] HttpIo The HttpIo wrapping the HTTP service.
279 @param[in] Request A pointer to storage such data as URL and HTTP method.
280 @param[in] HeaderCount Number of HTTP header structures in Headers list.
281 @param[in] Headers Array containing list of HTTP headers.
282 @param[in] BodyLength Length in bytes of the HTTP body.
283 @param[in] Body Body associated with the HTTP request.
284
285 @retval EFI_SUCCESS The HTTP request is trasmitted.
286 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
287 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
288 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
289 @retval Others Other errors as indicated.
290
291**/
292EFI_STATUS
293HttpIoSendRequest (
294 IN HTTP_IO *HttpIo,
295 IN EFI_HTTP_REQUEST_DATA *Request,
296 IN UINTN HeaderCount,
297 IN EFI_HTTP_HEADER *Headers,
298 IN UINTN BodyLength,
299 IN VOID *Body
300 )
301{
302 EFI_STATUS Status;
303 EFI_HTTP_PROTOCOL *Http;
304
305 if ((HttpIo == NULL) || (HttpIo->Http == NULL)) {
306 return EFI_INVALID_PARAMETER;
307 }
308
309 HttpIo->ReqToken.Status = EFI_NOT_READY;
310 HttpIo->ReqToken.Message->Data.Request = Request;
311 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
312 HttpIo->ReqToken.Message->Headers = Headers;
313 HttpIo->ReqToken.Message->BodyLength = BodyLength;
314 HttpIo->ReqToken.Message->Body = Body;
315
316 if (HttpIo->Callback != NULL) {
317 Status = HttpIo->Callback (
318 HttpIoRequest,
319 HttpIo->ReqToken.Message,
320 HttpIo->Context
321 );
322 if (EFI_ERROR (Status)) {
323 return Status;
324 }
325 }
326
327 //
328 // Queue the request token to HTTP instances.
329 //
330 Http = HttpIo->Http;
331 HttpIo->IsTxDone = FALSE;
332 Status = Http->Request (
333 Http,
334 &HttpIo->ReqToken
335 );
336 if (EFI_ERROR (Status)) {
337 return Status;
338 }
339
340 //
341 // Poll the network until transmit finish.
342 //
343 while (!HttpIo->IsTxDone) {
344 Http->Poll (Http);
345 }
346
347 return HttpIo->ReqToken.Status;
348}
349
350/**
351 Synchronously receive a HTTP RESPONSE message from the server.
352
353 @param[in] HttpIo The HttpIo wrapping the HTTP service.
354 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
355 FALSE to continue receive the previous response message.
356 @param[out] ResponseData Point to a wrapper of the received response data.
357
358 @retval EFI_SUCCESS The HTTP response is received.
359 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
360 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
361 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
362 @retval Others Other errors as indicated.
363
364**/
365EFI_STATUS
366HttpIoRecvResponse (
367 IN HTTP_IO *HttpIo,
368 IN BOOLEAN RecvMsgHeader,
369 OUT HTTP_IO_RESPONSE_DATA *ResponseData
370 )
371{
372 EFI_STATUS Status;
373 EFI_HTTP_PROTOCOL *Http;
374
375 if ((HttpIo == NULL) || (HttpIo->Http == NULL) || (ResponseData == NULL)) {
376 return EFI_INVALID_PARAMETER;
377 }
378
379 //
380 // Queue the response token to HTTP instances.
381 //
382 HttpIo->RspToken.Status = EFI_NOT_READY;
383 if (RecvMsgHeader) {
384 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
385 } else {
386 HttpIo->RspToken.Message->Data.Response = NULL;
387 }
388
389 HttpIo->RspToken.Message->HeaderCount = 0;
390 HttpIo->RspToken.Message->Headers = NULL;
391 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
392 HttpIo->RspToken.Message->Body = ResponseData->Body;
393
394 Http = HttpIo->Http;
395 HttpIo->IsRxDone = FALSE;
396
397 //
398 // Start the timer, and wait Timeout seconds to receive the header packet.
399 //
400 Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS);
401 if (EFI_ERROR (Status)) {
402 return Status;
403 }
404
405 Status = Http->Response (
406 Http,
407 &HttpIo->RspToken
408 );
409
410 if (EFI_ERROR (Status)) {
411 //
412 // Remove timeout timer from the event list.
413 //
414 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
415 return Status;
416 }
417
418 //
419 // Poll the network until receive finish.
420 //
421 while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent))) {
422 Http->Poll (Http);
423 }
424
425 //
426 // Remove timeout timer from the event list.
427 //
428 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
429
430 if (!HttpIo->IsRxDone) {
431 //
432 // Timeout occurs, cancel the response token.
433 //
434 Http->Cancel (Http, &HttpIo->RspToken);
435
436 Status = EFI_TIMEOUT;
437
438 return Status;
439 } else {
440 HttpIo->IsRxDone = FALSE;
441 }
442
443 if ((HttpIo->Callback != NULL) &&
444 ((HttpIo->RspToken.Status == EFI_SUCCESS) || (HttpIo->RspToken.Status == EFI_HTTP_ERROR)))
445 {
446 Status = HttpIo->Callback (
447 HttpIoResponse,
448 HttpIo->RspToken.Message,
449 HttpIo->Context
450 );
451 if (EFI_ERROR (Status)) {
452 return Status;
453 }
454 }
455
456 //
457 // Store the received data into the wrapper.
458 //
459 ResponseData->Status = HttpIo->RspToken.Status;
460 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
461 ResponseData->Headers = HttpIo->RspToken.Message->Headers;
462 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
463
464 return Status;
465}
466
467/**
468 Get the value of the content length if there is a "Content-Length" header.
469
470 @param[in] HeaderCount Number of HTTP header structures in Headers.
471 @param[in] Headers Array containing list of HTTP headers.
472 @param[out] ContentLength Pointer to save the value of the content length.
473
474 @retval EFI_SUCCESS Successfully get the content length.
475 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
476
477**/
478EFI_STATUS
479HttpIoGetContentLength (
480 IN UINTN HeaderCount,
481 IN EFI_HTTP_HEADER *Headers,
482 OUT UINTN *ContentLength
483 )
484{
485 EFI_HTTP_HEADER *Header;
486
487 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
488 if (Header == NULL) {
489 return EFI_NOT_FOUND;
490 }
491
492 return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **)NULL, ContentLength);
493}
494
495/**
496 Send HTTP request in chunks.
497
498 @param[in] HttpIo The HttpIo wrapping the HTTP service.
499 @param[in] SendChunkProcess Pointer to current chunk process status.
500 @param[in] RequestMessage Request to send.
501
502 @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.
503 @retval Other Other errors.
504
505**/
506EFI_STATUS
507HttpIoSendChunkedTransfer (
508 IN HTTP_IO *HttpIo,
509 IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
510 IN EFI_HTTP_MESSAGE *RequestMessage
511 )
512{
513 EFI_STATUS Status;
514 EFI_HTTP_HEADER *NewHeaders;
515 EFI_HTTP_HEADER *ContentLengthHeader;
516 UINTN AddNewHeader;
517 UINTN HeaderCount;
518 CHAR8 *MessageBody;
519 UINTN MessageBodyLength;
520 UINTN ChunkLength;
521 CHAR8 ChunkLengthStr[HTTP_IO_CHUNK_SIZE_STRING_LEN];
522 EFI_HTTP_REQUEST_DATA *SentRequestData;
523
524 AddNewHeader = 0;
525 NewHeaders = NULL;
526 MessageBody = NULL;
527 ContentLengthHeader = NULL;
528 MessageBodyLength = 0;
529
530 switch (*SendChunkProcess) {
531 case HttpIoSendChunkHeaderZeroContent:
532 ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH);
533 if (ContentLengthHeader == NULL) {
534 AddNewHeader = 1;
535 }
536
537 NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + AddNewHeader) * sizeof (EFI_HTTP_HEADER));
538 CopyMem ((VOID *)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
539 if (AddNewHeader == 0) {
540 //
541 // Override content-length to Transfer-Encoding.
542 //
543 ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);
544 ContentLengthHeader->FieldName = NULL;
545 ContentLengthHeader->FieldValue = NULL;
546 } else {
547 ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount;
548 }
549
550 HttpSetFieldNameAndValue (ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);
551 HeaderCount = RequestMessage->HeaderCount + AddNewHeader;
552 MessageBodyLength = 0;
553 MessageBody = NULL;
554 SentRequestData = RequestMessage->Data.Request;
555 break;
556
557 case HttpIoSendChunkContent:
558 HeaderCount = 0;
559 NewHeaders = NULL;
560 SentRequestData = NULL;
561 if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {
562 MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;
563 } else {
564 MessageBodyLength = RequestMessage->BodyLength;
565 }
566
567 AsciiSPrint (
568 ChunkLengthStr,
569 HTTP_IO_CHUNK_SIZE_STRING_LEN,
570 "%x%c%c",
571 MessageBodyLength,
572 CHUNKED_TRANSFER_CODING_CR,
573 CHUNKED_TRANSFER_CODING_LF
574 );
575 ChunkLength = AsciiStrLen (ChunkLengthStr);
576 MessageBody = AllocatePool (ChunkLength + MessageBodyLength + 2);
577 if (MessageBody == NULL) {
578 DEBUG ((DEBUG_ERROR, "Not enough memory for chunk transfer\n"));
579 return EFI_OUT_OF_RESOURCES;
580 }
581
582 //
583 // Build up the chunk transfer paylaod.
584 //
585 CopyMem (MessageBody, ChunkLengthStr, ChunkLength);
586 CopyMem (MessageBody + ChunkLength, RequestMessage->Body, MessageBodyLength);
587 *(MessageBody + ChunkLength + MessageBodyLength) = CHUNKED_TRANSFER_CODING_CR;
588 *(MessageBody + ChunkLength + MessageBodyLength + 1) = CHUNKED_TRANSFER_CODING_LF;
589 //
590 // Change variables for the next chunk trasnfer.
591 //
592 RequestMessage->BodyLength -= MessageBodyLength;
593 RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength);
594 MessageBodyLength += (ChunkLength + 2);
595 if (RequestMessage->BodyLength == 0) {
596 *SendChunkProcess = HttpIoSendChunkEndChunk;
597 }
598
599 break;
600
601 case HttpIoSendChunkEndChunk:
602 HeaderCount = 0;
603 NewHeaders = NULL;
604 SentRequestData = NULL;
605 AsciiSPrint (
606 ChunkLengthStr,
607 HTTP_IO_CHUNK_SIZE_STRING_LEN,
608 "0%c%c%c%c",
609 CHUNKED_TRANSFER_CODING_CR,
610 CHUNKED_TRANSFER_CODING_LF,
611 CHUNKED_TRANSFER_CODING_CR,
612 CHUNKED_TRANSFER_CODING_LF
613 );
614 MessageBody = AllocatePool (AsciiStrLen (ChunkLengthStr));
615 if (MessageBody == NULL) {
616 DEBUG ((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n"));
617 return EFI_OUT_OF_RESOURCES;
618 }
619
620 CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));
621 MessageBodyLength = AsciiStrLen (ChunkLengthStr);
622 *SendChunkProcess = HttpIoSendChunkFinish;
623 break;
624
625 default:
626 return EFI_INVALID_PARAMETER;
627 }
628
629 Status = HttpIoSendRequest (
630 HttpIo,
631 SentRequestData,
632 HeaderCount,
633 NewHeaders,
634 MessageBodyLength,
635 MessageBody
636 );
637 if (ContentLengthHeader != NULL) {
638 if (ContentLengthHeader->FieldName != NULL) {
639 FreePool (ContentLengthHeader->FieldName);
640 }
641
642 if (ContentLengthHeader->FieldValue != NULL) {
643 FreePool (ContentLengthHeader->FieldValue);
644 }
645 }
646
647 if (NewHeaders != NULL) {
648 FreePool (NewHeaders);
649 }
650
651 if (MessageBody != NULL) {
652 FreePool (MessageBody);
653 }
654
655 return Status;
656}
657
658/**
659 Synchronously receive a HTTP RESPONSE message from the server.
660
661 @param[in] HttpIo The HttpIo wrapping the HTTP service.
662 @param[in] HeaderCount Number of headers in Headers.
663 @param[in] Headers Array containing list of HTTP headers.
664 @param[out] ChunkListHead A pointer to receive list head
665 of chunked data. Caller has to
666 release memory of ChunkListHead
667 and all list entries.
668 @param[out] ContentLength Total content length
669
670 @retval EFI_SUCCESS The HTTP chunked transfer is received.
671 @retval EFI_NOT_FOUND No chunked transfer coding header found.
672 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
673 @retval EFI_INVALID_PARAMETER Improper parameters.
674 @retval Others Other errors as indicated.
675
676**/
677EFI_STATUS
678HttpIoGetChunkedTransferContent (
679 IN HTTP_IO *HttpIo,
680 IN UINTN HeaderCount,
681 IN EFI_HTTP_HEADER *Headers,
682 OUT LIST_ENTRY **ChunkListHead,
683 OUT UINTN *ContentLength
684 )
685{
686 EFI_HTTP_HEADER *Header;
687 CHAR8 ChunkSizeAscii[256];
688 EFI_STATUS Status;
689 UINTN Index;
690 HTTP_IO_RESPONSE_DATA ResponseData;
691 UINTN TotalLength;
692 UINTN MaxTotalLength;
693 LIST_ENTRY *HttpChunks;
694 HTTP_IO_CHUNKS *ThisChunk;
695 LIST_ENTRY *ThisListEntry;
696
697 if ((ChunkListHead == NULL) || (ContentLength == NULL)) {
698 return EFI_INVALID_PARAMETER;
699 }
700
701 *ContentLength = 0;
702 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
703 if (Header == NULL) {
704 return EFI_NOT_FOUND;
705 }
706
707 if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) {
708 return EFI_NOT_FOUND;
709 }
710
711 //
712 // Loop to get all chunks.
713 //
714 TotalLength = 0;
715 MaxTotalLength = PcdGet32 (PcdMaxHttpChunkTransfer);
716 HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
717 if (HttpChunks == NULL) {
718 Status = EFI_OUT_OF_RESOURCES;
719 goto ExitDeleteChunks;
720 }
721
722 InitializeListHead (HttpChunks);
723 DEBUG ((DEBUG_INFO, " Chunked transfer\n"));
724 while (TRUE) {
725 ZeroMem ((VOID *)&ResponseData, sizeof (HTTP_IO_RESPONSE_DATA));
726 ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;
727 ResponseData.Body = ChunkSizeAscii;
728 Status = HttpIoRecvResponse (
729 HttpIo,
730 FALSE,
731 &ResponseData
732 );
733 if (EFI_ERROR (Status)) {
734 goto ExitDeleteChunks;
735 }
736
737 //
738 // Decoding Chunked Transfer Coding.
739 // Only decode chunk-size and last chunk.
740 //
741 DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode));
742 //
743 // Break if this is last chunk.
744 //
745 if (ChunkSizeAscii[0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK) {
746 //
747 // Check if this is a valid Last-Chunk.
748 //
749 if ((ChunkSizeAscii[1] != CHUNKED_TRANSFER_CODING_CR) ||
750 (ChunkSizeAscii[2] != CHUNKED_TRANSFER_CODING_LF)
751 )
752 {
753 DEBUG ((DEBUG_ERROR, " This is an invalid Last-chunk\n"));
754 Status = EFI_INVALID_PARAMETER;
755 goto ExitDeleteChunks;
756 }
757
758 Status = EFI_SUCCESS;
759 DEBUG ((DEBUG_INFO, " Last-chunk\n"));
760 ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
761 if (ThisChunk == NULL) {
762 Status = EFI_OUT_OF_RESOURCES;
763 goto ExitDeleteChunks;
764 }
765
766 InitializeListHead (&ThisChunk->NextChunk);
767 ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF.
768 ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);
769 if (ThisChunk->Data == NULL) {
770 FreePool ((UINT8 *)ThisChunk);
771 Status = EFI_OUT_OF_RESOURCES;
772 goto ExitDeleteChunks;
773 }
774
775 CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length);
776 TotalLength += ThisChunk->Length;
777 InsertTailList (HttpChunks, &ThisChunk->NextChunk);
778 break;
779 }
780
781 //
782 // Get the chunk length
783 //
784 Index = 0;
785 while ((ChunkSizeAscii[Index] != CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) &&
786 (ChunkSizeAscii[Index] != (CHAR8)CHUNKED_TRANSFER_CODING_CR) &&
787 (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH))
788 {
789 Index++;
790 }
791
792 if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {
793 Status = EFI_NOT_FOUND;
794 goto ExitDeleteChunks;
795 }
796
797 ChunkSizeAscii[Index] = 0;
798 AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);
799 DEBUG ((DEBUG_INFO, " Length of this chunk %d\n", *ContentLength));
800 //
801 // Receive the data;
802 //
803 ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
804 if (ThisChunk == NULL) {
805 Status = EFI_OUT_OF_RESOURCES;
806 goto ExitDeleteChunks;
807 }
808
809 ResponseData.BodyLength = *ContentLength;
810 ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);
811 if (ResponseData.Body == NULL) {
812 FreePool (ThisChunk);
813 Status = EFI_OUT_OF_RESOURCES;
814 goto ExitDeleteChunks;
815 }
816
817 InitializeListHead (&ThisChunk->NextChunk);
818 ThisChunk->Length = *ContentLength;
819 ThisChunk->Data = ResponseData.Body;
820 InsertTailList (HttpChunks, &ThisChunk->NextChunk);
821 Status = HttpIoRecvResponse (
822 HttpIo,
823 FALSE,
824 &ResponseData
825 );
826 if (EFI_ERROR (Status)) {
827 goto ExitDeleteChunks;
828 }
829
830 //
831 // Read CRLF
832 //
833 ZeroMem ((VOID *)&ResponseData, sizeof (HTTP_IO_RESPONSE_DATA));
834 ResponseData.BodyLength = 2;
835 ResponseData.Body = ChunkSizeAscii;
836 Status = HttpIoRecvResponse (
837 HttpIo,
838 FALSE,
839 &ResponseData
840 );
841 if (EFI_ERROR (Status)) {
842 goto ExitDeleteChunks;
843 }
844
845 //
846 // Verify the end of chunk payload.
847 //
848 if ((ChunkSizeAscii[0] != CHUNKED_TRANSFER_CODING_CR) ||
849 (ChunkSizeAscii[1] != CHUNKED_TRANSFER_CODING_LF)
850 )
851 {
852 DEBUG ((DEBUG_ERROR, " This is an invalid End-of-chunk notation.\n"));
853 goto ExitDeleteChunks;
854 }
855
856 TotalLength += *ContentLength;
857 if (TotalLength > MaxTotalLength) {
858 DEBUG ((DEBUG_ERROR, " Total chunk transfer payload exceeds the size defined by PcdMaxHttpChunkTransfer.\n"));
859 goto ExitDeleteChunks;
860 }
861 }
862
863 *ContentLength = TotalLength;
864 *ChunkListHead = HttpChunks;
865 DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n", TotalLength));
866 return EFI_SUCCESS;
867
868ExitDeleteChunks:
869 if (HttpChunks != NULL) {
870 while (!IsListEmpty (HttpChunks)) {
871 ThisListEntry = GetFirstNode (HttpChunks);
872 RemoveEntryList (ThisListEntry);
873 ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry;
874 if (ThisChunk->Data != NULL) {
875 FreePool (ThisChunk->Data);
876 }
877
878 FreePool (ThisListEntry);
879 }
880
881 FreePool (HttpChunks);
882 }
883
884 return Status;
885}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette