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