]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpDxe/HttpImpl.c
CryptoPkg/SmmCryptLib: Enable AES support for SMM.
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpImpl.c
CommitLineData
47f51a06
YT
1/** @file\r
2 Implementation of EFI_HTTP_PROTOCOL protocol interfaces.\r
3\r
072289f4 4 Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
f58554fc 5 (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
47f51a06
YT
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php.\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "HttpDriver.h"\r
18\r
19EFI_HTTP_PROTOCOL mEfiHttpTemplate = {\r
20 EfiHttpGetModeData,\r
21 EfiHttpConfigure,\r
22 EfiHttpRequest,\r
23 EfiHttpCancel,\r
24 EfiHttpResponse,\r
25 EfiHttpPoll\r
26};\r
27\r
28/**\r
29 Returns the operational parameters for the current HTTP child instance.\r
30\r
31 The GetModeData() function is used to read the current mode data (operational\r
32 parameters) for this HTTP protocol instance.\r
33\r
34 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
35 @param[out] HttpConfigData Point to buffer for operational parameters of this\r
36 HTTP instance.\r
37\r
38 @retval EFI_SUCCESS Operation succeeded.\r
39 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
40 This is NULL.\r
41 HttpConfigData is NULL.\r
f0ab5a81
ZL
42 HttpInstance->LocalAddressIsIPv6 is FALSE and\r
43 HttpConfigData->IPv4Node is NULL.\r
44 HttpInstance->LocalAddressIsIPv6 is TRUE and\r
45 HttpConfigData->IPv6Node is NULL.\r
46 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
47f51a06
YT
47\r
48**/\r
49EFI_STATUS\r
50EFIAPI\r
51EfiHttpGetModeData (\r
52 IN EFI_HTTP_PROTOCOL *This,\r
53 OUT EFI_HTTP_CONFIG_DATA *HttpConfigData\r
54 )\r
55{\r
56 HTTP_PROTOCOL *HttpInstance;\r
57\r
f0ab5a81
ZL
58 //\r
59 // Check input parameters.\r
60 //\r
47f51a06
YT
61 if ((This == NULL) || (HttpConfigData == NULL)) {\r
62 return EFI_INVALID_PARAMETER;\r
63 }\r
f0ab5a81 64\r
47f51a06
YT
65 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
66 ASSERT (HttpInstance != NULL);\r
f0ab5a81
ZL
67\r
68 if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
69 (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
70 return EFI_INVALID_PARAMETER;\r
71 }\r
72\r
47f51a06
YT
73 if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
74 return EFI_NOT_STARTED;\r
75 }\r
76\r
47f51a06
YT
77 HttpConfigData->HttpVersion = HttpInstance->HttpVersion;\r
78 HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec;\r
79 HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;\r
80\r
b659408b 81 if (HttpInstance->LocalAddressIsIPv6) {\r
b659408b 82 CopyMem (\r
f0ab5a81 83 HttpConfigData->AccessPoint.IPv6Node,\r
b659408b
ZL
84 &HttpInstance->Ipv6Node,\r
85 sizeof (HttpInstance->Ipv6Node)\r
47f51a06 86 );\r
b659408b 87 } else {\r
b659408b 88 CopyMem (\r
f0ab5a81 89 HttpConfigData->AccessPoint.IPv4Node,\r
b659408b
ZL
90 &HttpInstance->IPv4Node,\r
91 sizeof (HttpInstance->IPv4Node)\r
92 );\r
b659408b 93 }\r
47f51a06
YT
94\r
95 return EFI_SUCCESS;\r
96}\r
97\r
98/**\r
99 Initialize or brutally reset the operational parameters for this EFI HTTP instance.\r
100\r
101 The Configure() function does the following:\r
102 When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring\r
103 timeout, local address, port, etc.\r
104 When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active\r
105 connections with remote hosts, canceling all asynchronous tokens, and flush request\r
106 and response buffers without informing the appropriate hosts.\r
107\r
f0ab5a81
ZL
108 No other EFI HTTP function can be executed by this instance until the Configure()\r
109 function is executed and returns successfully.\r
47f51a06
YT
110\r
111 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
112 @param[in] HttpConfigData Pointer to the configure data to configure the instance.\r
113\r
114 @retval EFI_SUCCESS Operation succeeded.\r
115 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
116 This is NULL.\r
f0ab5a81 117 HttpConfigData is NULL.\r
47f51a06
YT
118 HttpConfigData->LocalAddressIsIPv6 is FALSE and\r
119 HttpConfigData->IPv4Node is NULL.\r
120 HttpConfigData->LocalAddressIsIPv6 is TRUE and\r
121 HttpConfigData->IPv6Node is NULL.\r
122 @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling\r
123 Configure() with NULL to reset it.\r
124 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
125 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when\r
126 executing Configure().\r
127 @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported\r
128 in the implementation.\r
129**/\r
130EFI_STATUS\r
131EFIAPI\r
132EfiHttpConfigure (\r
133 IN EFI_HTTP_PROTOCOL *This,\r
134 IN EFI_HTTP_CONFIG_DATA *HttpConfigData\r
135 ) \r
136{\r
137 HTTP_PROTOCOL *HttpInstance;\r
138 EFI_STATUS Status;\r
b659408b
ZL
139 \r
140 //\r
141 // Check input parameters.\r
142 //\r
f0ab5a81
ZL
143 if (This == NULL ||\r
144 HttpConfigData == NULL ||\r
145 ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
146 (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL))) {\r
47f51a06
YT
147 return EFI_INVALID_PARAMETER;\r
148 }\r
149\r
150 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
151 ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);\r
152\r
153 if (HttpConfigData != NULL) {\r
b659408b 154\r
47f51a06
YT
155 //\r
156 // Now configure this HTTP instance.\r
157 //\r
158 if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {\r
159 return EFI_ALREADY_STARTED;\r
160 }\r
161\r
162 HttpInstance->HttpVersion = HttpConfigData->HttpVersion;\r
163 HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec;\r
164 HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;\r
b659408b
ZL
165 \r
166 if (HttpConfigData->LocalAddressIsIPv6) { \r
167 CopyMem (\r
168 &HttpInstance->Ipv6Node,\r
169 HttpConfigData->AccessPoint.IPv6Node,\r
170 sizeof (HttpInstance->Ipv6Node)\r
171 );\r
47f51a06
YT
172 } else {\r
173 CopyMem (\r
174 &HttpInstance->IPv4Node,\r
175 HttpConfigData->AccessPoint.IPv4Node,\r
176 sizeof (HttpInstance->IPv4Node)\r
177 );\r
47f51a06 178 }\r
b347a22a 179 \r
b659408b
ZL
180 //\r
181 // Creat Tcp child\r
182 //\r
183 Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6);\r
184 if (EFI_ERROR (Status)) {\r
185 return Status;\r
186 }\r
187 \r
188 HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;\r
189 return EFI_SUCCESS;\r
47f51a06
YT
190\r
191 } else {\r
b659408b
ZL
192 //\r
193 // Reset all the resources related to HttpInsance.\r
194 //\r
195 HttpCleanProtocol (HttpInstance);\r
196 HttpInstance->State = HTTP_STATE_UNCONFIGED;\r
197 return EFI_SUCCESS;\r
47f51a06
YT
198 }\r
199}\r
200 \r
201\r
202/**\r
203 The Request() function queues an HTTP request to this HTTP instance.\r
204\r
205 Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent\r
206 successfully, or if there is an error, Status in token will be updated and Event will\r
207 be signaled.\r
208\r
209 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
210 @param[in] Token Pointer to storage containing HTTP request token.\r
211\r
212 @retval EFI_SUCCESS Outgoing data was processed.\r
213 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
214 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
215 @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue.\r
216 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
217 @retval EFI_UNSUPPORTED The HTTP method is not supported in current\r
218 implementation.\r
219 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
220 This is NULL.\r
f0ab5a81 221 Token is NULL.\r
47f51a06
YT
222 Token->Message is NULL.\r
223 Token->Message->Body is not NULL,\r
224 Token->Message->BodyLength is non-zero, and\r
225 Token->Message->Data is NULL, but a previous call to\r
226 Request()has not been completed successfully.\r
227**/\r
228EFI_STATUS\r
229EFIAPI\r
230EfiHttpRequest (\r
231 IN EFI_HTTP_PROTOCOL *This,\r
232 IN EFI_HTTP_TOKEN *Token\r
233 )\r
234{\r
235 EFI_HTTP_MESSAGE *HttpMsg;\r
236 EFI_HTTP_REQUEST_DATA *Request;\r
237 VOID *UrlParser;\r
238 EFI_STATUS Status;\r
239 CHAR8 *HostName;\r
240 UINT16 RemotePort;\r
241 HTTP_PROTOCOL *HttpInstance;\r
242 BOOLEAN Configure;\r
243 BOOLEAN ReConfigure;\r
19c25725 244 CHAR8 *RequestMsg;\r
47f51a06 245 CHAR8 *Url;\r
51b0450e 246 UINTN UrlLen;\r
47f51a06
YT
247 CHAR16 *HostNameStr;\r
248 HTTP_TOKEN_WRAP *Wrap;\r
b199d941 249 CHAR8 *FileUrl;\r
19c25725 250 UINTN RequestMsgSize;\r
b199d941 251 \r
47f51a06
YT
252 if ((This == NULL) || (Token == NULL)) {\r
253 return EFI_INVALID_PARAMETER;\r
254 }\r
255\r
256 HttpMsg = Token->Message;\r
257 if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) {\r
258 return EFI_INVALID_PARAMETER;\r
259 }\r
260\r
261 //\r
262 // Current implementation does not support POST/PUT method.\r
263 // If future version supports these two methods, Request could be NULL for a special case that to send large amounts\r
264 // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.\r
265 // \r
266 //\r
267 Request = HttpMsg->Data.Request;\r
268 if ((Request == NULL) || (Request->Url == NULL)) {\r
269 return EFI_INVALID_PARAMETER;\r
270 }\r
271\r
272 //\r
273 // Only support GET and HEAD method in current implementation.\r
274 //\r
275 if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) {\r
276 return EFI_UNSUPPORTED;\r
277 }\r
278\r
279 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
280 ASSERT (HttpInstance != NULL);\r
281\r
282 if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {\r
283 return EFI_NOT_STARTED;\r
284 }\r
285\r
47f51a06
YT
286 //\r
287 // Check whether the token already existed.\r
288 //\r
289 if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {\r
290 return EFI_ACCESS_DENIED; \r
291 } \r
292\r
47f51a06
YT
293 HostName = NULL;\r
294 Wrap = NULL;\r
295 HostNameStr = NULL;\r
47f51a06
YT
296\r
297 //\r
298 // Parse the URI of the remote host.\r
299 //\r
7ef0690f 300 Url = HttpInstance->Url;\r
51b0450e
FS
301 UrlLen = StrLen (Request->Url) + 1;\r
302 if (UrlLen > HTTP_URL_BUFFER_LEN) {\r
303 Url = AllocateZeroPool (UrlLen);\r
304 if (Url == NULL) {\r
305 return EFI_OUT_OF_RESOURCES;\r
306 }\r
307 FreePool (HttpInstance->Url);\r
308 HttpInstance->Url = Url; \r
b659408b
ZL
309 } \r
310\r
47f51a06
YT
311\r
312 UnicodeStrToAsciiStr (Request->Url, Url);\r
313 UrlParser = NULL;\r
314 Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
315 if (EFI_ERROR (Status)) {\r
316 goto Error1;\r
317 }\r
318\r
19c25725 319 RequestMsg = NULL;\r
47f51a06
YT
320 HostName = NULL;\r
321 Status = HttpUrlGetHostName (Url, UrlParser, &HostName);\r
322 if (EFI_ERROR (Status)) {\r
323 goto Error1;\r
324 }\r
325\r
326 Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
327 if (EFI_ERROR (Status)) {\r
328 RemotePort = HTTP_DEFAULT_PORT;\r
329 }\r
a2e61982
ZL
330 //\r
331 // If Configure is TRUE, it indicates the first time to call Request();\r
332 // If ReConfigure is TRUE, it indicates the request URL is not same\r
333 // with the previous call to Request();\r
334 //\r
47f51a06
YT
335 Configure = TRUE;\r
336 ReConfigure = TRUE; \r
337\r
a8706acb 338 if (HttpInstance->RemoteHost == NULL) {\r
47f51a06
YT
339 //\r
340 // Request() is called the first time. \r
341 //\r
342 ReConfigure = FALSE;\r
343 } else {\r
344 if ((HttpInstance->RemotePort == RemotePort) &&\r
345 (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
346 //\r
347 // Host Name and port number of the request URL are the same with previous call to Request().\r
348 // Check whether previous TCP packet sent out.\r
349 //\r
350 if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {\r
351 //\r
352 // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
353 //\r
354 Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
355 if (Wrap == NULL) {\r
356 Status = EFI_OUT_OF_RESOURCES;\r
357 goto Error1;\r
358 }\r
359\r
360 Wrap->HttpToken = Token;\r
361 Wrap->HttpInstance = HttpInstance;\r
362\r
b659408b 363 Status = HttpCreateTcpTxEvent (Wrap);\r
47f51a06
YT
364 if (EFI_ERROR (Status)) {\r
365 goto Error1;\r
366 }\r
367\r
368 Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
369 if (EFI_ERROR (Status)) {\r
370 goto Error1;\r
371 }\r
372\r
373 Wrap->TcpWrap.Method = Request->Method;\r
374\r
47f51a06
YT
375 FreePool (HostName);\r
376 \r
377 //\r
378 // Queue the HTTP token and return.\r
379 //\r
380 return EFI_SUCCESS;\r
381 } else {\r
382 //\r
383 // Use existing TCP instance to transmit the packet.\r
384 //\r
385 Configure = FALSE;\r
386 ReConfigure = FALSE;\r
387 }\r
388 } else {\r
389 //\r
390 // Need close existing TCP instance and create a new TCP instance for data transmit.\r
391 //\r
392 if (HttpInstance->RemoteHost != NULL) {\r
393 FreePool (HttpInstance->RemoteHost);\r
394 HttpInstance->RemoteHost = NULL;\r
a8706acb 395 HttpInstance->RemotePort = 0;\r
47f51a06
YT
396 }\r
397 }\r
398 } \r
399\r
400 if (Configure) {\r
401 //\r
b659408b 402 // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.\r
47f51a06 403 //\r
b659408b
ZL
404 if (!HttpInstance->LocalAddressIsIPv6) {\r
405 Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);\r
406 } else {\r
ac458853 407 Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);\r
b659408b
ZL
408 }\r
409\r
47f51a06 410 if (EFI_ERROR (Status)) {\r
b659408b 411 HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));\r
47f51a06
YT
412 if (HostNameStr == NULL) {\r
413 Status = EFI_OUT_OF_RESOURCES;\r
414 goto Error1;\r
415 }\r
b659408b 416 \r
47f51a06 417 AsciiStrToUnicodeStr (HostName, HostNameStr);\r
b659408b
ZL
418 if (!HttpInstance->LocalAddressIsIPv6) {\r
419 Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);\r
420 } else {\r
421 Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);\r
422 }\r
423 \r
47f51a06
YT
424 FreePool (HostNameStr);\r
425 if (EFI_ERROR (Status)) {\r
426 goto Error1;\r
427 }\r
428 }\r
429\r
b659408b 430\r
47f51a06
YT
431 //\r
432 // Save the RemotePort and RemoteHost.\r
433 //\r
434 ASSERT (HttpInstance->RemoteHost == NULL);\r
435 HttpInstance->RemotePort = RemotePort;\r
436 HttpInstance->RemoteHost = HostName;\r
437 HostName = NULL;\r
438 }\r
439\r
440 if (ReConfigure) {\r
441 //\r
442 // The request URL is different from previous calls to Request(), close existing TCP instance.\r
443 //\r
a2e61982
ZL
444 if (!HttpInstance->LocalAddressIsIPv6) {\r
445 ASSERT (HttpInstance->Tcp4 != NULL);\r
446 } else {\r
447 ASSERT (HttpInstance->Tcp6 != NULL);\r
448 }\r
47f51a06
YT
449 HttpCloseConnection (HttpInstance);\r
450 EfiHttpCancel (This, NULL);\r
451 }\r
452\r
453 //\r
454 // Wrap the HTTP token in HTTP_TOKEN_WRAP\r
455 //\r
456 Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
457 if (Wrap == NULL) {\r
458 Status = EFI_OUT_OF_RESOURCES;\r
459 goto Error1;\r
460 }\r
461\r
462 Wrap->HttpToken = Token;\r
463 Wrap->HttpInstance = HttpInstance;\r
464 Wrap->TcpWrap.Method = Request->Method;\r
465\r
a2e61982
ZL
466 Status = HttpInitTcp (HttpInstance, Wrap, Configure);\r
467 if (EFI_ERROR (Status)) {\r
468 goto Error2;\r
469 } \r
b659408b 470\r
a2e61982 471 if (!Configure) {\r
47f51a06
YT
472 //\r
473 // For the new HTTP token, create TX TCP token events. \r
474 //\r
b659408b 475 Status = HttpCreateTcpTxEvent (Wrap);\r
47f51a06
YT
476 if (EFI_ERROR (Status)) {\r
477 goto Error1;\r
478 }\r
479 }\r
a2e61982 480 \r
47f51a06
YT
481 //\r
482 // Create request message.\r
483 //\r
b199d941
GCPL
484 FileUrl = Url;\r
485 if (*FileUrl != '/') {\r
486 //\r
487 // Convert the absolute-URI to the absolute-path\r
488 //\r
489 while (*FileUrl != ':') {\r
490 FileUrl++;\r
491 }\r
492 if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {\r
493 FileUrl += 3;\r
494 while (*FileUrl != '/') {\r
495 FileUrl++;\r
496 }\r
497 } else {\r
498 Status = EFI_INVALID_PARAMETER;\r
499 goto Error3;\r
500 }\r
501 }\r
f58554fc 502\r
19c25725 503 Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);\r
f58554fc
GB
504\r
505 if (EFI_ERROR (Status)) {\r
47f51a06
YT
506 goto Error3;\r
507 }\r
508\r
509 Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);\r
510 if (EFI_ERROR (Status)) {\r
511 goto Error4;\r
512 }\r
513\r
47f51a06
YT
514 //\r
515 // Transmit the request message.\r
516 //\r
b659408b 517 Status = HttpTransmitTcp (\r
47f51a06
YT
518 HttpInstance,\r
519 Wrap,\r
19c25725
NH
520 (UINT8*) RequestMsg,\r
521 RequestMsgSize\r
47f51a06
YT
522 );\r
523 if (EFI_ERROR (Status)) {\r
524 goto Error5; \r
525 }\r
526\r
49c9f74c 527 DispatchDpc ();\r
b659408b 528 \r
cdf8c32e
NH
529 if (HostName != NULL) {\r
530 FreePool (HostName);\r
531 }\r
b659408b 532 \r
47f51a06
YT
533 return EFI_SUCCESS;\r
534\r
535Error5:\r
536 NetMapRemoveTail (&HttpInstance->TxTokens, NULL);\r
537\r
538Error4:\r
19c25725
NH
539 if (RequestMsg != NULL) {\r
540 FreePool (RequestMsg);\r
47f51a06
YT
541 } \r
542\r
543Error3:\r
544 HttpCloseConnection (HttpInstance);\r
545\r
47f51a06 546Error2:\r
b659408b
ZL
547 HttpCloseTcpConnCloseEvent (HttpInstance);\r
548 if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {\r
549 gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
550 Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;\r
551 }\r
552 if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {\r
553 gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);\r
554 Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;\r
47f51a06
YT
555 }\r
556\r
557Error1:\r
b659408b 558\r
47f51a06
YT
559 if (HostName != NULL) {\r
560 FreePool (HostName);\r
561 }\r
562 if (Wrap != NULL) {\r
563 FreePool (Wrap);\r
564 }\r
565 if (UrlParser!= NULL) {\r
566 HttpUrlFreeParser (UrlParser);\r
567 }\r
568\r
569 return Status;\r
570 \r
571}\r
572\r
573/**\r
b659408b 574 Cancel a user's Token. \r
47f51a06
YT
575 \r
576 @param[in] Map The HTTP instance's token queue.\r
577 @param[in] Item Object container for one HTTP token and token's wrap.\r
578 @param[in] Context The user's token to cancel.\r
579\r
580 @retval EFI_SUCCESS Continue to check the next Item.\r
581 @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.\r
582\r
583**/\r
584EFI_STATUS\r
585EFIAPI\r
586HttpCancelTokens (\r
587 IN NET_MAP *Map,\r
588 IN NET_MAP_ITEM *Item,\r
589 IN VOID *Context\r
590 )\r
591{\r
592\r
593 EFI_HTTP_TOKEN *Token;\r
594 HTTP_TOKEN_WRAP *Wrap;\r
b659408b 595 HTTP_PROTOCOL *HttpInstance;\r
47f51a06
YT
596\r
597 Token = (EFI_HTTP_TOKEN *) Context;\r
598\r
599 //\r
600 // Return EFI_SUCCESS to check the next item in the map if\r
601 // this one doesn't match.\r
602 //\r
603 if ((Token != NULL) && (Token != Item->Key)) {\r
604 return EFI_SUCCESS;\r
605 }\r
606\r
607 Wrap = (HTTP_TOKEN_WRAP *) Item->Value;\r
608 ASSERT (Wrap != NULL);\r
b659408b 609 HttpInstance = Wrap->HttpInstance;\r
47f51a06
YT
610\r
611 //\r
612 // Free resources.\r
613 //\r
614 NetMapRemoveItem (Map, Item, NULL); \r
615 \r
b659408b
ZL
616 if (!HttpInstance->LocalAddressIsIPv6) {\r
617 if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) {\r
618 gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
619 }\r
620 \r
621 if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
622 gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
623 }\r
624 \r
625 if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
626 FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
627 }\r
47f51a06 628\r
b659408b
ZL
629 } else {\r
630 if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) {\r
631 gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);\r
632 }\r
633\r
634 if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
635 gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
636 }\r
47f51a06 637\r
b659408b
ZL
638 if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
639 FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
640 }\r
47f51a06
YT
641 }\r
642\r
b659408b 643\r
47f51a06
YT
644 FreePool (Wrap);\r
645\r
646 //\r
647 // If only one item is to be cancel, return EFI_ABORTED to stop\r
648 // iterating the map any more.\r
649 //\r
650 if (Token != NULL) {\r
651 return EFI_ABORTED;\r
652 }\r
653\r
654 return EFI_SUCCESS; \r
655}\r
656\r
657/**\r
658 Cancel the user's receive/transmit request. It is the worker function of\r
659 EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the\r
660 token.\r
661\r
662 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.\r
663 @param[in] Token The token to cancel. If NULL, all token will be\r
664 cancelled.\r
665\r
666 @retval EFI_SUCCESS The token is cancelled.\r
667 @retval EFI_NOT_FOUND The asynchronous request or response token is not found. \r
668 @retval Others Other error as indicated.\r
669\r
670**/\r
671EFI_STATUS\r
672HttpCancel (\r
673 IN HTTP_PROTOCOL *HttpInstance,\r
674 IN EFI_HTTP_TOKEN *Token\r
675 )\r
676{\r
677 EFI_STATUS Status;\r
678\r
679 //\r
680 // First check the tokens queued by EfiHttpRequest().\r
681 //\r
682 Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);\r
683 if (EFI_ERROR (Status)) {\r
684 if (Token != NULL) {\r
685 if (Status == EFI_ABORTED) {\r
686 return EFI_SUCCESS;\r
687 } \r
688 } else {\r
689 return Status;\r
690 }\r
691 }\r
692\r
693 //\r
694 // Then check the tokens queued by EfiHttpResponse().\r
695 //\r
696 Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
697 if (EFI_ERROR (Status)) {\r
698 if (Token != NULL) {\r
699 if (Status == EFI_ABORTED) {\r
700 return EFI_SUCCESS;\r
701 } else {\r
702 return EFI_NOT_FOUND;\r
703 }\r
704 } else {\r
705 return Status;\r
706 }\r
707 }\r
708\r
709 return EFI_SUCCESS;\r
710}\r
711\r
712\r
713/**\r
714 Abort an asynchronous HTTP request or response token.\r
715\r
716 The Cancel() function aborts a pending HTTP request or response transaction. If\r
717 Token is not NULL and the token is in transmit or receive queues when it is being\r
718 cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will\r
719 be signaled. If the token is not in one of the queues, which usually means that the\r
720 asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,\r
721 all asynchronous tokens issued by Request() or Response() will be aborted.\r
722\r
723 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
724 @param[in] Token Point to storage containing HTTP request or response\r
725 token.\r
726\r
727 @retval EFI_SUCCESS Request and Response queues are successfully flushed.\r
728 @retval EFI_INVALID_PARAMETER This is NULL.\r
729 @retval EFI_NOT_STARTED This instance hasn't been configured.\r
47f51a06
YT
730 @retval EFI_NOT_FOUND The asynchronous request or response token is not\r
731 found.\r
732 @retval EFI_UNSUPPORTED The implementation does not support this function.\r
733\r
734**/\r
735EFI_STATUS\r
736EFIAPI\r
737EfiHttpCancel (\r
738 IN EFI_HTTP_PROTOCOL *This,\r
739 IN EFI_HTTP_TOKEN *Token\r
740 )\r
741{\r
742 HTTP_PROTOCOL *HttpInstance;\r
743\r
744 if (This == NULL) {\r
745 return EFI_INVALID_PARAMETER;\r
746 }\r
747\r
748 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
749 ASSERT (HttpInstance != NULL);\r
750\r
751 if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
752 return EFI_NOT_STARTED;\r
753 }\r
754\r
755 return HttpCancel (HttpInstance, Token);\r
756\r
757}\r
758\r
759/**\r
760 A callback function to intercept events during message parser.\r
761\r
762 This function will be invoked during HttpParseMessageBody() with various events type. An error\r
763 return status of the callback function will cause the HttpParseMessageBody() aborted.\r
764\r
765 @param[in] EventType Event type of this callback call.\r
766 @param[in] Data A pointer to data buffer.\r
767 @param[in] Length Length in bytes of the Data.\r
768 @param[in] Context Callback context set by HttpInitMsgParser().\r
769\r
770 @retval EFI_SUCCESS Continue to parser the message body.\r
771\r
772**/\r
773EFI_STATUS\r
774EFIAPI\r
775HttpBodyParserCallback (\r
776 IN HTTP_BODY_PARSE_EVENT EventType,\r
777 IN CHAR8 *Data,\r
778 IN UINTN Length,\r
779 IN VOID *Context\r
780 )\r
781{\r
782 HTTP_TOKEN_WRAP *Wrap;\r
5ba9f065
ZL
783 UINTN BodyLength;\r
784 CHAR8 *Body;\r
47f51a06
YT
785\r
786 if (EventType != BodyParseEventOnComplete) {\r
787 return EFI_SUCCESS;\r
788 }\r
789\r
790 if (Data == NULL || Length != 0 || Context == NULL) {\r
791 return EFI_SUCCESS;\r
792 }\r
793\r
794 Wrap = (HTTP_TOKEN_WRAP *) Context;\r
5ba9f065
ZL
795 Body = Wrap->HttpToken->Message->Body;\r
796 BodyLength = Wrap->HttpToken->Message->BodyLength;\r
797 if (Data < Body + BodyLength) {\r
798 Wrap->HttpInstance->NextMsg = Data;\r
799 } else {\r
800 Wrap->HttpInstance->NextMsg = NULL;\r
801 }\r
802 \r
47f51a06
YT
803\r
804 //\r
b659408b 805 // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.\r
47f51a06
YT
806 //\r
807 FreePool (Wrap);\r
808\r
809 return EFI_SUCCESS;\r
810}\r
811\r
812/**\r
813 The work function of EfiHttpResponse().\r
814\r
815 @param[in] Wrap Pointer to HTTP token's wrap data.\r
816\r
817 @retval EFI_SUCCESS Allocation succeeded.\r
818 @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources.\r
b659408b 819 @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or \r
5ca29abe 820 the EFI_HTTP_UTILITIES_PROTOCOL is not available.\r
47f51a06
YT
821\r
822**/\r
823EFI_STATUS\r
824HttpResponseWorker (\r
825 IN HTTP_TOKEN_WRAP *Wrap\r
826 )\r
827{\r
828 EFI_STATUS Status;\r
829 EFI_HTTP_MESSAGE *HttpMsg;\r
47f51a06
YT
830 CHAR8 *EndofHeader;\r
831 CHAR8 *HttpHeaders;\r
832 UINTN SizeofHeaders;\r
47f51a06
YT
833 UINTN BufferSize;\r
834 UINTN StatusCode;\r
835 CHAR8 *Tmp;\r
836 CHAR8 *HeaderTmp;\r
837 CHAR8 *StatusCodeStr;\r
838 UINTN BodyLen;\r
839 HTTP_PROTOCOL *HttpInstance;\r
840 EFI_HTTP_TOKEN *Token;\r
841 NET_MAP_ITEM *Item;\r
842 HTTP_TOKEN_WRAP *ValueInItem;\r
843 UINTN HdrLen;\r
844\r
3fd7bd08 845 if (Wrap == NULL || Wrap->HttpInstance == NULL) {\r
846 return EFI_INVALID_PARAMETER;\r
847 }\r
848 \r
47f51a06
YT
849 HttpInstance = Wrap->HttpInstance;\r
850 Token = Wrap->HttpToken;\r
47f51a06
YT
851 HttpMsg = Token->Message;\r
852\r
b659408b
ZL
853 HttpInstance->EndofHeader = NULL;\r
854 HttpInstance->HttpHeaders = NULL;\r
855 HttpMsg->Headers = NULL;\r
856 HttpHeaders = NULL;\r
857 SizeofHeaders = 0;\r
858 BufferSize = 0;\r
859 EndofHeader = NULL;\r
47f51a06
YT
860 \r
861 if (HttpMsg->Data.Response != NULL) {\r
862 //\r
863 // Need receive the HTTP headers, prepare buffer.\r
864 //\r
b659408b 865 Status = HttpCreateTcpRxEventForHeader (HttpInstance);\r
47f51a06
YT
866 if (EFI_ERROR (Status)) {\r
867 goto Error;\r
868 }\r
869\r
870 //\r
871 // Check whether we have cached header from previous call.\r
872 //\r
873 if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {\r
874 //\r
875 // The data is stored at [NextMsg, CacheBody + CacheLen].\r
876 //\r
877 HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;\r
878 HttpHeaders = AllocateZeroPool (HdrLen);\r
879 if (HttpHeaders == NULL) {\r
880 Status = EFI_OUT_OF_RESOURCES;\r
881 goto Error;\r
882 }\r
883\r
884 CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);\r
885 FreePool (HttpInstance->CacheBody);\r
886 HttpInstance->CacheBody = NULL;\r
887 HttpInstance->NextMsg = NULL;\r
888 HttpInstance->CacheOffset = 0;\r
889 SizeofHeaders = HdrLen;\r
890 BufferSize = HttpInstance->CacheLen;\r
891\r
892 //\r
893 // Check whether we cached the whole HTTP headers.\r
894 //\r
895 EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); \r
b659408b 896 } \r
47f51a06 897\r
b659408b
ZL
898 HttpInstance->EndofHeader = &EndofHeader;\r
899 HttpInstance->HttpHeaders = &HttpHeaders;\r
47f51a06 900\r
b347a22a
JW
901\r
902 if (HttpInstance->TimeoutEvent == NULL) {\r
903 //\r
904 // Create TimeoutEvent for response\r
905 //\r
906 Status = gBS->CreateEvent (\r
907 EVT_TIMER,\r
908 TPL_CALLBACK,\r
909 NULL,\r
910 NULL,\r
911 &HttpInstance->TimeoutEvent\r
912 );\r
913 if (EFI_ERROR (Status)) {\r
914 goto Error;\r
915 }\r
916 }\r
917\r
918 //\r
919 // Start the timer, and wait Timeout seconds to receive the header packet.\r
920 //\r
921 Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
922 if (EFI_ERROR (Status)) {\r
923 goto Error;\r
924 }\r
925\r
926 Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);\r
927\r
928 gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
929\r
b659408b
ZL
930 if (EFI_ERROR (Status)) {\r
931 goto Error;\r
932 }\r
47f51a06 933\r
1b96428d
ZL
934 ASSERT (HttpHeaders != NULL);\r
935\r
47f51a06
YT
936 //\r
937 // Cache the part of body.\r
938 //\r
939 BodyLen = BufferSize - (EndofHeader - HttpHeaders);\r
940 if (BodyLen > 0) {\r
941 if (HttpInstance->CacheBody != NULL) {\r
942 FreePool (HttpInstance->CacheBody);\r
943 }\r
944\r
945 HttpInstance->CacheBody = AllocateZeroPool (BodyLen);\r
946 if (HttpInstance->CacheBody == NULL) {\r
947 Status = EFI_OUT_OF_RESOURCES;\r
948 goto Error;\r
949 }\r
950\r
951 CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);\r
952 HttpInstance->CacheLen = BodyLen;\r
953 }\r
954\r
47f51a06
YT
955 //\r
956 // Search for Status Code.\r
957 //\r
958 StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
959 if (StatusCodeStr == NULL) {\r
960 goto Error;\r
961 }\r
962\r
963 StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);\r
964\r
965 //\r
966 // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".\r
967 //\r
968 Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);\r
969 if (Tmp == NULL) {\r
970 goto Error;\r
971 }\r
972\r
973 Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);\r
974 SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);\r
975 HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
976 if (HeaderTmp == NULL) {\r
977 goto Error;\r
978 }\r
979\r
980 CopyMem (HeaderTmp, Tmp, SizeofHeaders);\r
981 FreePool (HttpHeaders);\r
982 HttpHeaders = HeaderTmp;\r
5ca29abe
JW
983\r
984 //\r
985 // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.\r
986 //\r
987 if (mHttpUtilities == NULL) {\r
988 Status = EFI_NOT_READY;\r
989 goto Error;\r
990 }\r
991 \r
47f51a06
YT
992 //\r
993 // Parse the HTTP header into array of key/value pairs.\r
994 //\r
5ca29abe
JW
995 Status = mHttpUtilities->Parse (\r
996 mHttpUtilities, \r
997 HttpHeaders, \r
998 SizeofHeaders, \r
999 &HttpMsg->Headers, \r
1000 &HttpMsg->HeaderCount\r
1001 );\r
47f51a06
YT
1002 if (EFI_ERROR (Status)) {\r
1003 goto Error;\r
1004 }\r
1005\r
1006 FreePool (HttpHeaders);\r
1007 HttpHeaders = NULL;\r
1008 \r
1009 HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);\r
072289f4 1010 HttpInstance->StatusCode = StatusCode;\r
47f51a06
YT
1011 //\r
1012 // Init message-body parser by header information. \r
1013 //\r
1014 Status = EFI_NOT_READY;\r
1015 ValueInItem = NULL;\r
1016 NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);\r
1017 if (ValueInItem == NULL) {\r
1018 goto Error;\r
1019 }\r
1020\r
1021 //\r
b659408b 1022 // The first Tx Token not transmitted yet, insert back and return error.\r
47f51a06
YT
1023 //\r
1024 if (!ValueInItem->TcpWrap.IsTxDone) {\r
1025 goto Error2;\r
1026 }\r
1027\r
1028 Status = HttpInitMsgParser (\r
1029 ValueInItem->TcpWrap.Method,\r
1030 HttpMsg->Data.Response->StatusCode,\r
1031 HttpMsg->HeaderCount,\r
1032 HttpMsg->Headers,\r
1033 HttpBodyParserCallback,\r
1034 (VOID *) ValueInItem,\r
1035 &HttpInstance->MsgParser\r
1036 );\r
1037 if (EFI_ERROR (Status)) { \r
1038 goto Error2;\r
1039 }\r
1040\r
1041 //\r
1042 // Check whether we received a complete HTTP message.\r
1043 //\r
1044 if (HttpInstance->CacheBody != NULL) {\r
1045 Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);\r
1046 if (EFI_ERROR (Status)) {\r
1047 goto Error2;\r
1048 }\r
1049\r
1050 if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
1051 //\r
1052 // Free the MsgParse since we already have a full HTTP message.\r
1053 //\r
1054 HttpFreeMsgParser (HttpInstance->MsgParser);\r
1055 HttpInstance->MsgParser = NULL;\r
1056 }\r
1057 }\r
1058\r
1059 if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { \r
1060 Status = EFI_SUCCESS;\r
1061 goto Exit;\r
1062 }\r
1063 } \r
1064\r
1065 //\r
1066 // Receive the response body.\r
1067 //\r
1068 BodyLen = 0;\r
1069\r
1070 //\r
1071 // First check whether we cached some data.\r
1072 //\r
1073 if (HttpInstance->CacheBody != NULL) {\r
1074 //\r
1075 // Calculate the length of the cached data.\r
1076 //\r
1077 if (HttpInstance->NextMsg != NULL) {\r
1078 //\r
1079 // We have a cached HTTP message which includes a part of HTTP header of next message.\r
1080 //\r
1081 BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); \r
1082 } else {\r
1083 BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;\r
1084 }\r
1085\r
1086 if (BodyLen > 0) {\r
1087 //\r
1088 // We have some cached data. Just copy the data and return.\r
1089 //\r
1090 if (HttpMsg->BodyLength < BodyLen) {\r
1091 CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);\r
1092 HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;\r
1093 } else {\r
1094 //\r
1095 // Copy all cached data out.\r
1096 //\r
1097 CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);\r
1098 HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;\r
1099 HttpMsg->BodyLength = BodyLen;\r
1100\r
1101 if (HttpInstance->NextMsg == NULL) {\r
1102 //\r
1103 // There is no HTTP header of next message. Just free the cache buffer.\r
1104 //\r
1105 FreePool (HttpInstance->CacheBody);\r
1106 HttpInstance->CacheBody = NULL;\r
1107 HttpInstance->NextMsg = NULL;\r
1108 HttpInstance->CacheOffset = 0;\r
1109 }\r
1110 }\r
1111 //\r
1112 // Return since we aready received required data.\r
1113 //\r
1114 Status = EFI_SUCCESS;\r
1115 goto Exit;\r
1116 } \r
1117\r
1118 if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {\r
1119 //\r
1120 // We received a complete HTTP message, and we don't have more data to return to caller.\r
1121 //\r
1122 HttpMsg->BodyLength = 0;\r
1123 Status = EFI_SUCCESS;\r
1124 goto Exit; \r
1125 } \r
1126 }\r
1127\r
1128 ASSERT (HttpInstance->MsgParser != NULL);\r
1129\r
b347a22a
JW
1130 if (HttpInstance->TimeoutEvent == NULL) {\r
1131 //\r
1132 // Create TimeoutEvent for response\r
1133 //\r
1134 Status = gBS->CreateEvent (\r
1135 EVT_TIMER,\r
1136 TPL_CALLBACK,\r
1137 NULL,\r
1138 NULL,\r
1139 &HttpInstance->TimeoutEvent\r
1140 );\r
1141 if (EFI_ERROR (Status)) {\r
1142 goto Error;\r
1143 }\r
1144 }\r
1145\r
1146 //\r
1147 // Start the timer, and wait Timeout seconds to receive the body packet.\r
1148 //\r
1149 Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
1150 if (EFI_ERROR (Status)) {\r
1151 goto Error;\r
1152 }\r
1153\r
47f51a06
YT
1154 //\r
1155 // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
1156 //\r
b347a22a
JW
1157 Status = HttpTcpReceiveBody (Wrap, HttpMsg, HttpInstance->TimeoutEvent);\r
1158\r
1159 gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
1160\r
47f51a06 1161 if (EFI_ERROR (Status)) {\r
47f51a06
YT
1162 goto Error;\r
1163 }\r
1164\r
1165 return Status;\r
1166\r
1167Exit:\r
1168 Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);\r
1169 if (Item != NULL) {\r
1170 NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
1171 }\r
072289f4
ZL
1172\r
1173 if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {\r
1174 Token->Status = EFI_HTTP_ERROR;\r
1175 } else {\r
1176 Token->Status = Status;\r
1177 }\r
1178\r
47f51a06 1179 gBS->SignalEvent (Token->Event);\r
b659408b 1180 HttpCloseTcpRxEvent (Wrap);\r
47f51a06
YT
1181 FreePool (Wrap);\r
1182 return Status;\r
1183\r
1184Error2:\r
1185 NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);\r
1186\r
1187Error:\r
b659408b 1188 HttpTcpTokenCleanup (Wrap);\r
47f51a06
YT
1189 \r
1190 if (HttpHeaders != NULL) {\r
1191 FreePool (HttpHeaders);\r
1192 }\r
1193\r
1194 if (HttpMsg->Headers != NULL) {\r
1195 FreePool (HttpMsg->Headers);\r
1196 }\r
1197\r
1198 if (HttpInstance->CacheBody != NULL) {\r
1199 FreePool (HttpInstance->CacheBody);\r
1200 HttpInstance->CacheBody = NULL;\r
1201 }\r
1202\r
072289f4
ZL
1203 if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {\r
1204 Token->Status = EFI_HTTP_ERROR;\r
1205 } else {\r
1206 Token->Status = Status;\r
1207 }\r
1208\r
47f51a06
YT
1209 gBS->SignalEvent (Token->Event);\r
1210\r
1211 return Status; \r
1212\r
1213}\r
1214\r
1215\r
1216/**\r
1217 The Response() function queues an HTTP response to this HTTP instance, similar to\r
f0ab5a81 1218 Receive() function in the EFI TCP driver. When the HTTP response is received successfully,\r
47f51a06
YT
1219 or if there is an error, Status in token will be updated and Event will be signaled.\r
1220\r
1221 The HTTP driver will queue a receive token to the underlying TCP instance. When data\r
1222 is received in the underlying TCP instance, the data will be parsed and Token will\r
1223 be populated with the response data. If the data received from the remote host\r
1224 contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting\r
1225 (asynchronously) for more data to be sent from the remote host before signaling\r
1226 Event in Token.\r
1227\r
1228 It is the responsibility of the caller to allocate a buffer for Body and specify the\r
1229 size in BodyLength. If the remote host provides a response that contains a content\r
1230 body, up to BodyLength bytes will be copied from the receive buffer into Body and\r
1231 BodyLength will be updated with the amount of bytes received and copied to Body. This\r
1232 allows the client to download a large file in chunks instead of into one contiguous\r
1233 block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is\r
1234 non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive\r
1235 token to underlying TCP instance. If data arrives in the receive buffer, up to\r
1236 BodyLength bytes of data will be copied to Body. The HTTP driver will then update\r
1237 BodyLength with the amount of bytes received and copied to Body.\r
1238\r
1239 If the HTTP driver does not have an open underlying TCP connection with the host\r
1240 specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is\r
1241 consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain\r
1242 an open TCP connection between client and host.\r
1243\r
1244 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
1245 @param[in] Token Pointer to storage containing HTTP response token.\r
1246\r
1247 @retval EFI_SUCCESS Allocation succeeded.\r
1248 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been\r
1249 initialized.\r
1250 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
1251 This is NULL.\r
1252 Token is NULL.\r
1253 Token->Message->Headers is NULL.\r
1254 Token->Message is NULL.\r
1255 Token->Message->Body is not NULL,\r
1256 Token->Message->BodyLength is non-zero, and\r
1257 Token->Message->Data is NULL, but a previous call to\r
1258 Response() has not been completed successfully.\r
1259 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
1260 @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host\r
1261 specified by response URL.\r
1262**/\r
1263EFI_STATUS\r
1264EFIAPI\r
1265EfiHttpResponse (\r
1266 IN EFI_HTTP_PROTOCOL *This,\r
1267 IN EFI_HTTP_TOKEN *Token\r
1268 )\r
1269{\r
1270 EFI_STATUS Status;\r
1271 EFI_HTTP_MESSAGE *HttpMsg;\r
1272 HTTP_PROTOCOL *HttpInstance;\r
1273 HTTP_TOKEN_WRAP *Wrap;\r
1274\r
1275 if ((This == NULL) || (Token == NULL)) {\r
1276 return EFI_INVALID_PARAMETER;\r
1277 }\r
1278\r
1279 HttpMsg = Token->Message;\r
1280 if (HttpMsg == NULL) {\r
1281 return EFI_INVALID_PARAMETER;\r
1282 }\r
1283 \r
1284 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
1285 ASSERT (HttpInstance != NULL);\r
1286\r
1287 if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
1288 return EFI_NOT_STARTED;\r
1289 }\r
1290\r
47f51a06
YT
1291 //\r
1292 // Check whether the token already existed.\r
1293 //\r
1294 if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {\r
1295 return EFI_ACCESS_DENIED; \r
1296 }\r
1297\r
1298 Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));\r
1299 if (Wrap == NULL) {\r
1300 return EFI_OUT_OF_RESOURCES;\r
1301 }\r
1302\r
1303 Wrap->HttpInstance = HttpInstance;\r
1304 Wrap->HttpToken = Token;\r
1305\r
b659408b 1306 Status = HttpCreateTcpRxEvent (Wrap);\r
47f51a06
YT
1307 if (EFI_ERROR (Status)) {\r
1308 goto Error;\r
1309 }\r
1310\r
1311 Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);\r
1312 if (EFI_ERROR (Status)) {\r
1313 goto Error;\r
1314 }\r
1315\r
1316 //\r
1317 // If already have pending RxTokens, return directly.\r
1318 //\r
1319 if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {\r
1320 return EFI_SUCCESS;\r
1321 }\r
1322\r
1323 return HttpResponseWorker (Wrap);\r
1324\r
1325Error:\r
1326 if (Wrap != NULL) {\r
b659408b
ZL
1327 if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
1328 gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
1329 }\r
1330\r
1331 if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
1332 gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
47f51a06
YT
1333 }\r
1334 FreePool (Wrap);\r
1335 } \r
1336\r
1337 return Status; \r
1338}\r
1339\r
1340/**\r
1341 The Poll() function can be used by network drivers and applications to increase the\r
1342 rate that data packets are moved between the communication devices and the transmit\r
1343 and receive queues.\r
1344\r
1345 In some systems, the periodic timer event in the managed network driver may not poll\r
1346 the underlying communications device fast enough to transmit and/or receive all data\r
1347 packets without missing incoming packets or dropping outgoing packets. Drivers and\r
1348 applications that are experiencing packet loss should try calling the Poll() function\r
1349 more often.\r
1350\r
1351 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
1352\r
1353 @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
1354 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
1355 @retval EFI_INVALID_PARAMETER This is NULL.\r
1356 @retval EFI_NOT_READY No incoming or outgoing data is processed.\r
1357 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
1358\r
1359**/\r
1360EFI_STATUS\r
1361EFIAPI\r
1362EfiHttpPoll (\r
1363 IN EFI_HTTP_PROTOCOL *This\r
1364 )\r
1365{\r
49c9f74c 1366 EFI_STATUS Status;\r
b659408b 1367 HTTP_PROTOCOL *HttpInstance;\r
47f51a06
YT
1368\r
1369 if (This == NULL) {\r
1370 return EFI_INVALID_PARAMETER;\r
1371 }\r
1372\r
1373 HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
1374 ASSERT (HttpInstance != NULL);\r
1375\r
1b96428d 1376 if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
47f51a06
YT
1377 return EFI_NOT_STARTED;\r
1378 }\r
b659408b
ZL
1379 \r
1380 if (HttpInstance->LocalAddressIsIPv6) {\r
1b96428d
ZL
1381 if (HttpInstance->Tcp6 == NULL) {\r
1382 return EFI_NOT_STARTED;\r
1383 }\r
b659408b
ZL
1384 Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);\r
1385 } else {\r
1b96428d
ZL
1386 if (HttpInstance->Tcp4 == NULL) {\r
1387 return EFI_NOT_STARTED;\r
1388 }\r
b659408b
ZL
1389 Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);\r
1390 }\r
1391 \r
49c9f74c 1392 DispatchDpc ();\r
b659408b 1393 \r
49c9f74c 1394 return Status;\r
47f51a06 1395}\r