]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
NetworkPkg/Library: Implementation of Http IO Helper Library
[mirror_edk2.git] / NetworkPkg / Library / DxeHttpIoLib / DxeHttpIoLib.c
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 **/
26 VOID
27 EFIAPI
28 HttpIoNotifyDpc (
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 **/
42 VOID
43 EFIAPI
44 HttpIoNotify (
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 **/
61 VOID
62 HttpIoDestroyIo (
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 **/
129 EFI_STATUS
130 HttpIoCreateIo (
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
193 if (ConfigData != NULL) {
194 if (HttpIo->IpVersion == IP_VERSION_4) {
195 HttpConfigData.LocalAddressIsIPv6 = FALSE;
196 HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion;
197 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
198
199 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
200 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
201 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
202 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
203 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
204 } else {
205 HttpConfigData.LocalAddressIsIPv6 = TRUE;
206 HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion;
207 HttpConfigData.TimeOutMillisec = ConfigData->Config6.RequestTimeOut;
208
209 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
210 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
211 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
212 }
213
214 Status = Http->Configure (Http, &HttpConfigData);
215 if (EFI_ERROR (Status)) {
216 goto ON_ERROR;
217 }
218 }
219
220 //
221 // Create events for variuos asynchronous operations.
222 //
223 Status = gBS->CreateEvent (
224 EVT_NOTIFY_SIGNAL,
225 TPL_NOTIFY,
226 HttpIoNotify,
227 &HttpIo->IsTxDone,
228 &Event
229 );
230 if (EFI_ERROR (Status)) {
231 goto ON_ERROR;
232 }
233 HttpIo->ReqToken.Event = Event;
234 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
235
236 Status = gBS->CreateEvent (
237 EVT_NOTIFY_SIGNAL,
238 TPL_NOTIFY,
239 HttpIoNotify,
240 &HttpIo->IsRxDone,
241 &Event
242 );
243 if (EFI_ERROR (Status)) {
244 goto ON_ERROR;
245 }
246 HttpIo->RspToken.Event = Event;
247 HttpIo->RspToken.Message = &HttpIo->RspMessage;
248
249 //
250 // Create TimeoutEvent for response
251 //
252 Status = gBS->CreateEvent (
253 EVT_TIMER,
254 TPL_CALLBACK,
255 NULL,
256 NULL,
257 &Event
258 );
259 if (EFI_ERROR (Status)) {
260 goto ON_ERROR;
261 }
262 HttpIo->TimeoutEvent = Event;
263 return EFI_SUCCESS;
264
265 ON_ERROR:
266 HttpIoDestroyIo (HttpIo);
267
268 return Status;
269 }
270
271 /**
272 Synchronously send a HTTP REQUEST message to the server.
273
274 @param[in] HttpIo The HttpIo wrapping the HTTP service.
275 @param[in] Request A pointer to storage such data as URL and HTTP method.
276 @param[in] HeaderCount Number of HTTP header structures in Headers list.
277 @param[in] Headers Array containing list of HTTP headers.
278 @param[in] BodyLength Length in bytes of the HTTP body.
279 @param[in] Body Body associated with the HTTP request.
280
281 @retval EFI_SUCCESS The HTTP request is trasmitted.
282 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
283 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
284 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
285 @retval Others Other errors as indicated.
286
287 **/
288 EFI_STATUS
289 HttpIoSendRequest (
290 IN HTTP_IO *HttpIo,
291 IN EFI_HTTP_REQUEST_DATA *Request,
292 IN UINTN HeaderCount,
293 IN EFI_HTTP_HEADER *Headers,
294 IN UINTN BodyLength,
295 IN VOID *Body
296 )
297 {
298 EFI_STATUS Status;
299 EFI_HTTP_PROTOCOL *Http;
300
301 if (HttpIo == NULL || HttpIo->Http == NULL) {
302 return EFI_INVALID_PARAMETER;
303 }
304
305 HttpIo->ReqToken.Status = EFI_NOT_READY;
306 HttpIo->ReqToken.Message->Data.Request = Request;
307 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
308 HttpIo->ReqToken.Message->Headers = Headers;
309 HttpIo->ReqToken.Message->BodyLength = BodyLength;
310 HttpIo->ReqToken.Message->Body = Body;
311
312 if (HttpIo->Callback != NULL) {
313 Status = HttpIo->Callback (
314 HttpIoRequest,
315 HttpIo->ReqToken.Message,
316 HttpIo->Context
317 );
318 if (EFI_ERROR (Status)) {
319 return Status;
320 }
321 }
322
323 //
324 // Queue the request token to HTTP instances.
325 //
326 Http = HttpIo->Http;
327 HttpIo->IsTxDone = FALSE;
328 Status = Http->Request (
329 Http,
330 &HttpIo->ReqToken
331 );
332 if (EFI_ERROR (Status)) {
333 return Status;
334 }
335
336 //
337 // Poll the network until transmit finish.
338 //
339 while (!HttpIo->IsTxDone) {
340 Http->Poll (Http);
341 }
342
343 return HttpIo->ReqToken.Status;
344 }
345
346 /**
347 Synchronously receive a HTTP RESPONSE message from the server.
348
349 @param[in] HttpIo The HttpIo wrapping the HTTP service.
350 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
351 FALSE to continue receive the previous response message.
352 @param[out] ResponseData Point to a wrapper of the received response data.
353
354 @retval EFI_SUCCESS The HTTP response is received.
355 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
356 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
357 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
358 @retval Others Other errors as indicated.
359
360 **/
361 EFI_STATUS
362 HttpIoRecvResponse (
363 IN HTTP_IO *HttpIo,
364 IN BOOLEAN RecvMsgHeader,
365 OUT HTTP_IO_RESPONSE_DATA *ResponseData
366 )
367 {
368 EFI_STATUS Status;
369 EFI_HTTP_PROTOCOL *Http;
370
371 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
372 return EFI_INVALID_PARAMETER;
373 }
374
375 //
376 // Queue the response token to HTTP instances.
377 //
378 HttpIo->RspToken.Status = EFI_NOT_READY;
379 if (RecvMsgHeader) {
380 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
381 } else {
382 HttpIo->RspToken.Message->Data.Response = NULL;
383 }
384 HttpIo->RspToken.Message->HeaderCount = 0;
385 HttpIo->RspToken.Message->Headers = NULL;
386 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
387 HttpIo->RspToken.Message->Body = ResponseData->Body;
388
389 Http = HttpIo->Http;
390 HttpIo->IsRxDone = FALSE;
391
392 //
393 // Start the timer, and wait Timeout seconds to receive the header packet.
394 //
395 Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS);
396 if (EFI_ERROR (Status)) {
397 return Status;
398 }
399
400 Status = Http->Response (
401 Http,
402 &HttpIo->RspToken
403 );
404
405 if (EFI_ERROR (Status)) {
406 //
407 // Remove timeout timer from the event list.
408 //
409 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
410 return Status;
411 }
412
413 //
414 // Poll the network until receive finish.
415 //
416 while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent))) {
417 Http->Poll (Http);
418 }
419
420 //
421 // Remove timeout timer from the event list.
422 //
423 gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
424
425 if (!HttpIo->IsRxDone) {
426 //
427 // Timeout occurs, cancel the response token.
428 //
429 Http->Cancel (Http, &HttpIo->RspToken);
430
431 Status = EFI_TIMEOUT;
432
433 return Status;
434 } else {
435 HttpIo->IsRxDone = FALSE;
436 }
437
438 if ((HttpIo->Callback != NULL) &&
439 (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) {
440 Status = HttpIo->Callback (
441 HttpIoResponse,
442 HttpIo->RspToken.Message,
443 HttpIo->Context
444 );
445 if (EFI_ERROR (Status)) {
446 return Status;
447 }
448 }
449
450 //
451 // Store the received data into the wrapper.
452 //
453 ResponseData->Status = HttpIo->RspToken.Status;
454 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
455 ResponseData->Headers = HttpIo->RspToken.Message->Headers;
456 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
457
458 return Status;
459 }
460
461 /**
462 Get the value of the content length if there is a "Content-Length" header.
463
464 @param[in] HeaderCount Number of HTTP header structures in Headers.
465 @param[in] Headers Array containing list of HTTP headers.
466 @param[out] ContentLength Pointer to save the value of the content length.
467
468 @retval EFI_SUCCESS Successfully get the content length.
469 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
470
471 **/
472 EFI_STATUS
473 HttpIoGetContentLength (
474 IN UINTN HeaderCount,
475 IN EFI_HTTP_HEADER *Headers,
476 OUT UINTN *ContentLength
477 )
478 {
479 EFI_HTTP_HEADER *Header;
480
481 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
482 if (Header == NULL) {
483 return EFI_NOT_FOUND;
484 }
485
486 return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);
487 }
488 /**
489 Send HTTP request in chunks.
490
491 @param[in] HttpIo The HttpIo wrapping the HTTP service.
492 @param[in] SendChunkProcess Pointer to current chunk process status.
493 @param[in] RequestMessage Request to send.
494
495 @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.
496 @retval Other Other errors.
497
498 **/
499 EFI_STATUS
500 HttpIoSendChunkedTransfer (
501 IN HTTP_IO *HttpIo,
502 IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
503 IN EFI_HTTP_MESSAGE *RequestMessage
504 )
505 {
506 EFI_STATUS Status;
507 EFI_HTTP_HEADER *NewHeaders;
508 EFI_HTTP_HEADER *ContentLengthHeader;
509 UINTN AddNewHeader;
510 UINTN HeaderCount;
511 CHAR8 *MessageBody;
512 UINTN MessageBodyLength;
513 UINTN ChunkLength;
514 CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN];
515 EFI_HTTP_REQUEST_DATA *SentRequestData;
516
517 AddNewHeader = 0;
518 NewHeaders = NULL;
519 MessageBody = NULL;
520 ContentLengthHeader = NULL;
521 MessageBodyLength = 0;
522
523 switch (*SendChunkProcess) {
524 case HttpIoSendChunkHeaderZeroContent:
525 ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH);
526 if (ContentLengthHeader == NULL) {
527 AddNewHeader = 1;
528 }
529
530 NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + AddNewHeader) * sizeof(EFI_HTTP_HEADER));
531 CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
532 if (AddNewHeader == 0) {
533 //
534 // Override content-length to Transfer-Encoding.
535 //
536 ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);
537 ContentLengthHeader->FieldName = NULL;
538 ContentLengthHeader->FieldValue = NULL;
539 } else {
540 ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount;
541 }
542 HttpSetFieldNameAndValue (ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);
543 HeaderCount = RequestMessage->HeaderCount + AddNewHeader;
544 MessageBodyLength = 0;
545 MessageBody = NULL;
546 SentRequestData = RequestMessage->Data.Request;
547 break;
548
549 case HttpIoSendChunkContent:
550 HeaderCount = 0;
551 NewHeaders = NULL;
552 SentRequestData = NULL;
553 if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {
554 MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;
555 } else {
556 MessageBodyLength = RequestMessage->BodyLength;
557 }
558 AsciiSPrint (
559 ChunkLengthStr,
560 HTTP_IO_CHUNK_SIZE_STRING_LEN,
561 "%x%c%c",
562 MessageBodyLength,
563 CHUNKED_TRANSFER_CODING_CR,
564 CHUNKED_TRANSFER_CODING_LF
565 );
566 ChunkLength = AsciiStrLen (ChunkLengthStr);
567 MessageBody = AllocatePool (ChunkLength + MessageBodyLength + 2);
568 if (MessageBody == NULL) {
569 DEBUG((DEBUG_ERROR, "Not enough memory for chunk transfer\n"));
570 return EFI_OUT_OF_RESOURCES;
571 }
572 //
573 // Build up the chunk transfer paylaod.
574 //
575 CopyMem (MessageBody, ChunkLengthStr, ChunkLength);
576 CopyMem (MessageBody + ChunkLength, RequestMessage->Body, MessageBodyLength);
577 *(MessageBody + ChunkLength + MessageBodyLength) = CHUNKED_TRANSFER_CODING_CR;
578 *(MessageBody + ChunkLength + MessageBodyLength + 1) = CHUNKED_TRANSFER_CODING_LF;
579 //
580 // Change variables for the next chunk trasnfer.
581 //
582 RequestMessage->BodyLength -= MessageBodyLength;
583 RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength);
584 MessageBodyLength += (ChunkLength + 2);
585 if (RequestMessage->BodyLength == 0) {
586 *SendChunkProcess = HttpIoSendChunkEndChunk;
587 }
588 break;
589
590 case HttpIoSendChunkEndChunk:
591 HeaderCount = 0;
592 NewHeaders = NULL;
593 SentRequestData = NULL;
594 AsciiSPrint (
595 ChunkLengthStr,
596 HTTP_IO_CHUNK_SIZE_STRING_LEN,
597 "0%c%c%c%c",
598 CHUNKED_TRANSFER_CODING_CR,
599 CHUNKED_TRANSFER_CODING_LF,
600 CHUNKED_TRANSFER_CODING_CR,
601 CHUNKED_TRANSFER_CODING_LF
602 );
603 MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr));
604 if (MessageBody == NULL) {
605 DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n"));
606 return EFI_OUT_OF_RESOURCES;
607 }
608 CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));
609 MessageBodyLength = AsciiStrLen (ChunkLengthStr);
610 *SendChunkProcess = HttpIoSendChunkFinish;
611 break;
612
613 default:
614 return EFI_INVALID_PARAMETER;
615 }
616
617 Status = HttpIoSendRequest (
618 HttpIo,
619 SentRequestData,
620 HeaderCount,
621 NewHeaders,
622 MessageBodyLength,
623 MessageBody
624 );
625 if (ContentLengthHeader != NULL) {
626 if (ContentLengthHeader->FieldName != NULL) {
627 FreePool (ContentLengthHeader->FieldName);
628 }
629 if (ContentLengthHeader->FieldValue != NULL) {
630 FreePool (ContentLengthHeader->FieldValue);
631 }
632 }
633 if (NewHeaders != NULL) {
634 FreePool (NewHeaders);
635 }
636 if (MessageBody != NULL) {
637 FreePool (MessageBody);
638 }
639 return Status;
640 }
641
642 /**
643 Synchronously receive a HTTP RESPONSE message from the server.
644
645 @param[in] HttpIo The HttpIo wrapping the HTTP service.
646 @param[in] HeaderCount Number of headers in Headers.
647 @param[in] Headers Array containing list of HTTP headers.
648 @param[out] ChunkListHead A pointer to receive list head
649 of chunked data. Caller has to
650 release memory of ChunkListHead
651 and all list entries.
652 @param[out] ContentLength Total content length
653
654 @retval EFI_SUCCESS The HTTP chunked transfer is received.
655 @retval EFI_NOT_FOUND No chunked transfer coding header found.
656 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
657 @retval EFI_INVALID_PARAMETER Improper parameters.
658 @retval Others Other errors as indicated.
659
660 **/
661 EFI_STATUS
662 HttpIoGetChunkedTransferContent (
663 IN HTTP_IO *HttpIo,
664 IN UINTN HeaderCount,
665 IN EFI_HTTP_HEADER *Headers,
666 OUT LIST_ENTRY **ChunkListHead,
667 OUT UINTN *ContentLength
668 )
669 {
670 EFI_HTTP_HEADER *Header;
671 CHAR8 ChunkSizeAscii [256];
672 EFI_STATUS Status;
673 UINTN Index;
674 HTTP_IO_RESPONSE_DATA ResponseData;
675 UINTN TotalLength;
676 UINTN MaxTotalLength;
677 LIST_ENTRY *HttpChunks;
678 HTTP_IO_CHUNKS *ThisChunk;
679 LIST_ENTRY *ThisListEntry;
680
681 if (ChunkListHead == NULL || ContentLength == NULL) {
682 return EFI_INVALID_PARAMETER;
683 }
684
685 *ContentLength = 0;
686 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
687 if (Header == NULL) {
688 return EFI_NOT_FOUND;
689 }
690
691 if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) {
692 return EFI_NOT_FOUND;
693 }
694 //
695 // Loop to get all chunks.
696 //
697 TotalLength = 0;
698 MaxTotalLength = PcdGet32 (PcdMaxHttpChunkTransfer);
699 HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
700 if (HttpChunks == NULL) {
701 Status = EFI_OUT_OF_RESOURCES;
702 goto ExitDeleteChunks;
703 }
704 InitializeListHead (HttpChunks);
705 DEBUG ((DEBUG_INFO, " Chunked transfer\n"));
706 while (TRUE) {
707 ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
708 ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;
709 ResponseData.Body = ChunkSizeAscii;
710 Status = HttpIoRecvResponse (
711 HttpIo,
712 FALSE,
713 &ResponseData
714 );
715 if (EFI_ERROR (Status)) {
716 goto ExitDeleteChunks;
717 }
718 //
719 // Decoding Chunked Transfer Coding.
720 // Only decode chunk-size and last chunk.
721 //
722 DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode));
723 //
724 // Break if this is last chunk.
725 //
726 if (ChunkSizeAscii [0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK) {
727 //
728 // Check if this is a valid Last-Chunk.
729 //
730 if ((ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_CR) ||
731 (ChunkSizeAscii [2] != CHUNKED_TRANSFER_CODING_LF)
732 ) {
733 DEBUG ((DEBUG_ERROR, " This is an invalid Last-chunk\n"));
734 Status = EFI_INVALID_PARAMETER;
735 goto ExitDeleteChunks;
736 }
737
738 Status = EFI_SUCCESS;
739 DEBUG ((DEBUG_INFO, " Last-chunk\n"));
740 ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
741 if (ThisChunk == NULL) {
742 Status = EFI_OUT_OF_RESOURCES;
743 goto ExitDeleteChunks;
744 }
745
746 InitializeListHead (&ThisChunk->NextChunk);
747 ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF.
748 ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);
749 if (ThisChunk->Data == NULL) {
750 FreePool ((UINT8 *)ThisChunk);
751 Status = EFI_OUT_OF_RESOURCES;
752 goto ExitDeleteChunks;
753 }
754 CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length);
755 TotalLength += ThisChunk->Length;
756 InsertTailList (HttpChunks, &ThisChunk->NextChunk);
757 break;
758 }
759
760 //
761 // Get the chunk length
762 //
763 Index = 0;
764 while ((ChunkSizeAscii [Index] != CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) &&
765 (ChunkSizeAscii [Index] != (CHAR8)CHUNKED_TRANSFER_CODING_CR) &&
766 (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH)) {
767 Index ++;
768 }
769
770 if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {
771 Status = EFI_NOT_FOUND;
772 goto ExitDeleteChunks;
773 }
774 ChunkSizeAscii[Index] = 0;
775 AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);
776 DEBUG ((DEBUG_INFO, " Length of this chunk %d\n", *ContentLength));
777 //
778 // Receive the data;
779 //
780 ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
781 if (ThisChunk == NULL) {
782 Status = EFI_OUT_OF_RESOURCES;
783 goto ExitDeleteChunks;
784 }
785 ResponseData.BodyLength = *ContentLength;
786 ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);
787 if (ResponseData.Body == NULL) {
788 FreePool (ThisChunk);
789 Status = EFI_OUT_OF_RESOURCES;
790 goto ExitDeleteChunks;
791 }
792 InitializeListHead (&ThisChunk->NextChunk);
793 ThisChunk->Length = *ContentLength;
794 ThisChunk->Data = ResponseData.Body;
795 InsertTailList (HttpChunks, &ThisChunk->NextChunk);
796 Status = HttpIoRecvResponse (
797 HttpIo,
798 FALSE,
799 &ResponseData
800 );
801 if (EFI_ERROR (Status)) {
802 goto ExitDeleteChunks;
803 }
804 //
805 // Read CRLF
806 //
807 ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
808 ResponseData.BodyLength = 2;
809 ResponseData.Body = ChunkSizeAscii;
810 Status = HttpIoRecvResponse (
811 HttpIo,
812 FALSE,
813 &ResponseData
814 );
815 if (EFI_ERROR (Status)) {
816 goto ExitDeleteChunks;
817 }
818 //
819 // Verify the end of chunk payload.
820 //
821 if ((ChunkSizeAscii [0] != CHUNKED_TRANSFER_CODING_CR) ||
822 (ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_LF)
823 ) {
824 DEBUG ((DEBUG_ERROR, " This is an invalid End-of-chunk notation.\n"));
825 goto ExitDeleteChunks;
826 }
827 TotalLength += *ContentLength;
828 if (TotalLength > MaxTotalLength) {
829 DEBUG ((DEBUG_ERROR, " Total chunk transfer payload exceeds the size defined by PcdMaxHttpChunkTransfer.\n"));
830 goto ExitDeleteChunks;
831 }
832 }
833
834 *ContentLength = TotalLength;
835 *ChunkListHead = HttpChunks;
836 DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n", TotalLength));
837 return EFI_SUCCESS;
838
839 ExitDeleteChunks:
840 if (HttpChunks != NULL) {
841 while (!IsListEmpty(HttpChunks)) {
842 ThisListEntry = GetFirstNode (HttpChunks);
843 RemoveEntryList (ThisListEntry);
844 ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry;
845 if (ThisChunk->Data != NULL) {
846 FreePool (ThisChunk->Data);
847 }
848 FreePool(ThisListEntry);
849 }
850 FreePool (HttpChunks);
851 }
852 return Status;
853 }