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