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