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