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
HttpIoNotifyDpc(IN VOID * Context)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
HttpIoNotify(IN EFI_EVENT Event,IN VOID * Context)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
HttpIoDestroyIo(IN HTTP_IO * HttpIo)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
HttpIoCreateIo(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN UINT8 IpVersion,IN HTTP_IO_CONFIG_DATA * ConfigData,OPTIONAL IN HTTP_IO_CALLBACK Callback,IN VOID * Context,OUT HTTP_IO * HttpIo)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
HttpIoSendRequest(IN HTTP_IO * HttpIo,IN EFI_HTTP_REQUEST_DATA * Request,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN UINTN BodyLength,IN VOID * Body)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
HttpIoRecvResponse(IN HTTP_IO * HttpIo,IN BOOLEAN RecvMsgHeader,OUT HTTP_IO_RESPONSE_DATA * ResponseData)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
HttpIoGetContentLength(IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,OUT UINTN * ContentLength)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
HttpIoSendChunkedTransfer(IN HTTP_IO * HttpIo,IN HTTP_IO_SEND_CHUNK_PROCESS * SendChunkProcess,IN EFI_HTTP_MESSAGE * RequestMessage)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
HttpIoGetChunkedTransferContent(IN HTTP_IO * HttpIo,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,OUT LIST_ENTRY ** ChunkListHead,OUT UINTN * ContentLength)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 }
855