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