• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
EfiHttpGetModeData(IN EFI_HTTP_PROTOCOL * This,OUT EFI_HTTP_CONFIG_DATA * HttpConfigData)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->LocalAddressIsIPv6 is FALSE and
118                                   HttpConfigData->IPv4Node is NULL.
119                                   HttpConfigData->LocalAddressIsIPv6 is TRUE and
120                                   HttpConfigData->IPv6Node is NULL.
121   @retval EFI_ALREADY_STARTED     Reinitialize this HTTP instance without calling
122                                   Configure() with NULL to reset it.
123   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
124   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources when
125                                   executing Configure().
126   @retval EFI_UNSUPPORTED         One or more options in HttpConfigData are not supported
127                                   in the implementation.
128 **/
129 EFI_STATUS
130 EFIAPI
EfiHttpConfigure(IN EFI_HTTP_PROTOCOL * This,IN EFI_HTTP_CONFIG_DATA * HttpConfigData)131 EfiHttpConfigure (
132   IN  EFI_HTTP_PROTOCOL         *This,
133   IN  EFI_HTTP_CONFIG_DATA      *HttpConfigData
134   )
135 {
136   HTTP_PROTOCOL                 *HttpInstance;
137   EFI_STATUS                    Status;
138 
139   //
140   // Check input parameters.
141   //
142   if (This == NULL ||
143       (HttpConfigData != NULL &&
144        ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
145         (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {
146     return EFI_INVALID_PARAMETER;
147   }
148 
149   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
150   ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);
151 
152   if (HttpConfigData != NULL) {
153 
154     //
155     // Now configure this HTTP instance.
156     //
157     if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {
158       return EFI_ALREADY_STARTED;
159     }
160 
161     HttpInstance->HttpVersion        = HttpConfigData->HttpVersion;
162     HttpInstance->TimeOutMillisec    = HttpConfigData->TimeOutMillisec;
163     HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
164 
165     if (HttpConfigData->LocalAddressIsIPv6) {
166       CopyMem (
167         &HttpInstance->Ipv6Node,
168         HttpConfigData->AccessPoint.IPv6Node,
169         sizeof (HttpInstance->Ipv6Node)
170         );
171     } else {
172       CopyMem (
173         &HttpInstance->IPv4Node,
174         HttpConfigData->AccessPoint.IPv4Node,
175         sizeof (HttpInstance->IPv4Node)
176         );
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 is NULL.
221                                   Token->Message is NULL.
222                                   Token->Message->Body is not NULL,
223                                   Token->Message->BodyLength is non-zero, and
224                                   Token->Message->Data is NULL, but a previous call to
225                                   Request()has not been completed successfully.
226 **/
227 EFI_STATUS
228 EFIAPI
EfiHttpRequest(IN EFI_HTTP_PROTOCOL * This,IN EFI_HTTP_TOKEN * Token)229 EfiHttpRequest (
230   IN  EFI_HTTP_PROTOCOL         *This,
231   IN  EFI_HTTP_TOKEN            *Token
232   )
233 {
234   EFI_HTTP_MESSAGE              *HttpMsg;
235   EFI_HTTP_REQUEST_DATA         *Request;
236   VOID                          *UrlParser;
237   EFI_STATUS                    Status;
238   CHAR8                         *HostName;
239   UINTN                         HostNameSize;
240   UINT16                        RemotePort;
241   HTTP_PROTOCOL                 *HttpInstance;
242   BOOLEAN                       Configure;
243   BOOLEAN                       ReConfigure;
244   BOOLEAN                       TlsConfigure;
245   CHAR8                         *RequestMsg;
246   CHAR8                         *Url;
247   UINTN                         UrlLen;
248   CHAR16                        *HostNameStr;
249   HTTP_TOKEN_WRAP               *Wrap;
250   CHAR8                         *FileUrl;
251   UINTN                         RequestMsgSize;
252 
253   //
254   // Initializations
255   //
256   Url = NULL;
257   UrlParser = NULL;
258   RemotePort = 0;
259   HostName = NULL;
260   RequestMsg = NULL;
261   HostNameStr = NULL;
262   Wrap = NULL;
263   FileUrl = NULL;
264   TlsConfigure = FALSE;
265 
266   if ((This == NULL) || (Token == NULL)) {
267     return EFI_INVALID_PARAMETER;
268   }
269 
270   HttpMsg = Token->Message;
271   if (HttpMsg == NULL) {
272     return EFI_INVALID_PARAMETER;
273   }
274 
275   Request = HttpMsg->Data.Request;
276 
277   //
278   // Only support GET, HEAD, PUT and POST method in current implementation.
279   //
280   if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
281       (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost)) {
282     return EFI_UNSUPPORTED;
283   }
284 
285   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
286   ASSERT (HttpInstance != NULL);
287 
288   //
289   // Capture the method into HttpInstance.
290   //
291   if (Request != NULL) {
292     HttpInstance->Method = Request->Method;
293   }
294 
295   if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
296     return EFI_NOT_STARTED;
297   }
298 
299   if (Request == NULL) {
300     //
301     // Request would be NULL only for PUT/POST operation (in the current implementation)
302     //
303     if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {
304       return EFI_INVALID_PARAMETER;
305     }
306 
307     //
308     // For PUT/POST, we need to have the TCP already configured. Bail out if it is not!
309     //
310     if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
311       return EFI_INVALID_PARAMETER;
312     }
313 
314     //
315     // We need to have the Message Body for sending the HTTP message across in these cases.
316     //
317     if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {
318       return EFI_INVALID_PARAMETER;
319     }
320 
321     //
322     // Use existing TCP instance to transmit the packet.
323     //
324     Configure   = FALSE;
325     ReConfigure = FALSE;
326   } else {
327     //
328     // Check whether the token already existed.
329     //
330     if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
331       return EFI_ACCESS_DENIED;
332     }
333 
334     //
335     // Parse the URI of the remote host.
336     //
337     Url = HttpInstance->Url;
338     UrlLen = StrLen (Request->Url) + 1;
339     if (UrlLen > HTTP_URL_BUFFER_LEN) {
340       Url = AllocateZeroPool (UrlLen);
341       if (Url == NULL) {
342         return EFI_OUT_OF_RESOURCES;
343       }
344       FreePool (HttpInstance->Url);
345       HttpInstance->Url = Url;
346     }
347 
348 
349     UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);
350 
351     //
352     // From the information in Url, the HTTP instance will
353     // be able to determine whether to use http or https.
354     //
355     HttpInstance->UseHttps = IsHttpsUrl (Url);
356 
357     //
358     // Check whether we need to create Tls child and open the TLS protocol.
359     //
360     if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {
361       //
362       // Use TlsSb to create Tls child and open the TLS protocol.
363       //
364       HttpInstance->TlsChildHandle = TlsCreateChild (
365                                        HttpInstance->Service->ImageHandle,
366                                        &(HttpInstance->Tls),
367                                        &(HttpInstance->TlsConfiguration)
368                                        );
369       if (HttpInstance->TlsChildHandle == NULL) {
370         return EFI_DEVICE_ERROR;
371       }
372 
373       TlsConfigure = TRUE;
374     }
375 
376     UrlParser = NULL;
377     Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
378     if (EFI_ERROR (Status)) {
379       goto Error1;
380     }
381 
382     HostName   = NULL;
383     Status     = HttpUrlGetHostName (Url, UrlParser, &HostName);
384     if (EFI_ERROR (Status)) {
385      goto Error1;
386     }
387 
388     Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
389     if (EFI_ERROR (Status)) {
390       if (HttpInstance->UseHttps) {
391         RemotePort = HTTPS_DEFAULT_PORT;
392       } else {
393         RemotePort = HTTP_DEFAULT_PORT;
394       }
395     }
396     //
397     // If Configure is TRUE, it indicates the first time to call Request();
398     // If ReConfigure is TRUE, it indicates the request URL is not same
399     // with the previous call to Request();
400     //
401     Configure   = TRUE;
402     ReConfigure = TRUE;
403 
404     if (HttpInstance->RemoteHost == NULL) {
405       //
406       // Request() is called the first time.
407       //
408       ReConfigure = FALSE;
409     } else {
410       if ((HttpInstance->RemotePort == RemotePort) &&
411           (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) &&
412           (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
413                                        !TlsConfigure &&
414                                        HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {
415         //
416         // Host Name and port number of the request URL are the same with previous call to Request().
417         // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
418         // Check whether previous TCP packet sent out.
419         //
420 
421         if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
422           //
423           // Wrap the HTTP token in HTTP_TOKEN_WRAP
424           //
425           Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
426           if (Wrap == NULL) {
427             Status = EFI_OUT_OF_RESOURCES;
428             goto Error1;
429           }
430 
431           Wrap->HttpToken    = Token;
432           Wrap->HttpInstance = HttpInstance;
433 
434           Status = HttpCreateTcpTxEvent (Wrap);
435           if (EFI_ERROR (Status)) {
436             goto Error1;
437           }
438 
439           Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
440           if (EFI_ERROR (Status)) {
441             goto Error1;
442           }
443 
444           Wrap->TcpWrap.Method = Request->Method;
445 
446           FreePool (HostName);
447 
448           //
449           // Queue the HTTP token and return.
450           //
451           return EFI_SUCCESS;
452         } else {
453           //
454           // Use existing TCP instance to transmit the packet.
455           //
456           Configure   = FALSE;
457           ReConfigure = FALSE;
458         }
459       } else {
460         //
461         // Need close existing TCP instance and create a new TCP instance for data transmit.
462         //
463         if (HttpInstance->RemoteHost != NULL) {
464           FreePool (HttpInstance->RemoteHost);
465           HttpInstance->RemoteHost = NULL;
466           HttpInstance->RemotePort = 0;
467         }
468       }
469     }
470   }
471 
472   if (Configure) {
473     //
474     // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
475     //
476     if (!HttpInstance->LocalAddressIsIPv6) {
477       Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
478     } else {
479       Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
480     }
481 
482     if (EFI_ERROR (Status)) {
483       HostNameSize = AsciiStrSize (HostName);
484       HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
485       if (HostNameStr == NULL) {
486         Status = EFI_OUT_OF_RESOURCES;
487         goto Error1;
488       }
489 
490       AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
491       if (!HttpInstance->LocalAddressIsIPv6) {
492         Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
493       } else {
494         Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);
495       }
496 
497       FreePool (HostNameStr);
498       if (EFI_ERROR (Status)) {
499         goto Error1;
500       }
501     }
502 
503     //
504     // Save the RemotePort and RemoteHost.
505     //
506     ASSERT (HttpInstance->RemoteHost == NULL);
507     HttpInstance->RemotePort = RemotePort;
508     HttpInstance->RemoteHost = HostName;
509     HostName = NULL;
510   }
511 
512   if (ReConfigure) {
513     //
514     // The request URL is different from previous calls to Request(), close existing TCP instance.
515     //
516     if (!HttpInstance->LocalAddressIsIPv6) {
517       ASSERT (HttpInstance->Tcp4 != NULL);
518     } else {
519       ASSERT (HttpInstance->Tcp6 != NULL);
520     }
521 
522     if (HttpInstance->UseHttps && !TlsConfigure) {
523       Status = TlsCloseSession (HttpInstance);
524       if (EFI_ERROR (Status)) {
525         goto Error1;
526       }
527 
528       TlsCloseTxRxEvent (HttpInstance);
529     }
530 
531     HttpCloseConnection (HttpInstance);
532     EfiHttpCancel (This, NULL);
533   }
534 
535   //
536   // Wrap the HTTP token in HTTP_TOKEN_WRAP
537   //
538   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
539   if (Wrap == NULL) {
540     Status = EFI_OUT_OF_RESOURCES;
541     goto Error1;
542   }
543 
544   Wrap->HttpToken      = Token;
545   Wrap->HttpInstance   = HttpInstance;
546   if (Request != NULL) {
547     Wrap->TcpWrap.Method = Request->Method;
548   }
549 
550   Status = HttpInitSession (
551              HttpInstance,
552              Wrap,
553              Configure || ReConfigure,
554              TlsConfigure
555              );
556   if (EFI_ERROR (Status)) {
557     goto Error2;
558   }
559 
560   if (!Configure && !ReConfigure && !TlsConfigure) {
561     //
562     // For the new HTTP token, create TX TCP token events.
563     //
564     Status = HttpCreateTcpTxEvent (Wrap);
565     if (EFI_ERROR (Status)) {
566       goto Error1;
567     }
568   }
569 
570   //
571   // Create request message.
572   //
573   FileUrl = Url;
574   if (Url != NULL && *FileUrl != '/') {
575     //
576     // Convert the absolute-URI to the absolute-path
577     //
578     while (*FileUrl != ':') {
579       FileUrl++;
580     }
581     if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
582       FileUrl += 3;
583       while (*FileUrl != '/') {
584         FileUrl++;
585       }
586     } else {
587       Status = EFI_INVALID_PARAMETER;
588       goto Error3;
589     }
590   }
591 
592   Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
593 
594   if (EFI_ERROR (Status) || NULL == RequestMsg) {
595     goto Error3;
596   }
597 
598   ASSERT (RequestMsg != NULL);
599 
600   //
601   // Every request we insert a TxToken and a response call would remove the TxToken.
602   // In cases of PUT/POST, after an initial request-response pair, we would do a
603   // continuous request without a response call. So, in such cases, where Request
604   // structure is NULL, we would not insert a TxToken.
605   //
606   if (Request != NULL) {
607     Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
608     if (EFI_ERROR (Status)) {
609       goto Error4;
610     }
611   }
612 
613   //
614   // Transmit the request message.
615   //
616   Status = HttpTransmitTcp (
617              HttpInstance,
618              Wrap,
619              (UINT8*) RequestMsg,
620              RequestMsgSize
621              );
622   if (EFI_ERROR (Status)) {
623     goto Error5;
624   }
625 
626   DispatchDpc ();
627 
628   if (HostName != NULL) {
629     FreePool (HostName);
630   }
631 
632   return EFI_SUCCESS;
633 
634 Error5:
635   //
636   // We would have inserted a TxToken only if Request structure is not NULL.
637   // Hence check before we do a remove in this error case.
638   //
639   if (Request != NULL) {
640     NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
641   }
642 
643 Error4:
644   if (RequestMsg != NULL) {
645     FreePool (RequestMsg);
646   }
647 
648 Error3:
649   if (HttpInstance->UseHttps) {
650     TlsCloseSession (HttpInstance);
651     TlsCloseTxRxEvent (HttpInstance);
652   }
653 
654 Error2:
655   HttpCloseConnection (HttpInstance);
656 
657   HttpCloseTcpConnCloseEvent (HttpInstance);
658   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
659     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
660     Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;
661   }
662   if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {
663     gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
664     Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;
665   }
666 
667 Error1:
668   if (HostName != NULL) {
669     FreePool (HostName);
670   }
671   if (Wrap != NULL) {
672     FreePool (Wrap);
673   }
674   if (UrlParser!= NULL) {
675     HttpUrlFreeParser (UrlParser);
676   }
677 
678   return Status;
679 
680 }
681 
682 /**
683   Cancel a user's Token.
684 
685   @param[in]  Map                The HTTP instance's token queue.
686   @param[in]  Item               Object container for one HTTP token and token's wrap.
687   @param[in]  Context            The user's token to cancel.
688 
689   @retval EFI_SUCCESS            Continue to check the next Item.
690   @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.
691 
692 **/
693 EFI_STATUS
694 EFIAPI
HttpCancelTokens(IN NET_MAP * Map,IN NET_MAP_ITEM * Item,IN VOID * Context)695 HttpCancelTokens (
696   IN NET_MAP                *Map,
697   IN NET_MAP_ITEM           *Item,
698   IN VOID                   *Context
699   )
700 {
701   EFI_HTTP_TOKEN            *Token;
702   HTTP_TOKEN_WRAP           *Wrap;
703   HTTP_PROTOCOL             *HttpInstance;
704 
705   Token = (EFI_HTTP_TOKEN *) Context;
706 
707   //
708   // Return EFI_SUCCESS to check the next item in the map if
709   // this one doesn't match.
710   //
711   if ((Token != NULL) && (Token != Item->Key)) {
712     return EFI_SUCCESS;
713   }
714 
715   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;
716   ASSERT (Wrap != NULL);
717   HttpInstance = Wrap->HttpInstance;
718 
719   if (!HttpInstance->LocalAddressIsIPv6) {
720     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
721       //
722       // Cancle the Token before close its Event.
723       //
724       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
725 
726       //
727       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
728       //
729       DispatchDpc ();
730     }
731   } else {
732     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
733       //
734       // Cancle the Token before close its Event.
735       //
736       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
737 
738       //
739       // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
740       //
741       DispatchDpc ();
742     }
743   }
744 
745   //
746   // If only one item is to be cancel, return EFI_ABORTED to stop
747   // iterating the map any more.
748   //
749   if (Token != NULL) {
750     return EFI_ABORTED;
751   }
752 
753   return EFI_SUCCESS;
754 }
755 
756 /**
757   Cancel the user's receive/transmit request. It is the worker function of
758   EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
759   token.
760 
761   @param[in]  HttpInstance       Pointer to HTTP_PROTOCOL structure.
762   @param[in]  Token              The token to cancel. If NULL, all token will be
763                                  cancelled.
764 
765   @retval EFI_SUCCESS            The token is cancelled.
766   @retval EFI_NOT_FOUND          The asynchronous request or response token is not found.
767   @retval Others                 Other error as indicated.
768 
769 **/
770 EFI_STATUS
HttpCancel(IN HTTP_PROTOCOL * HttpInstance,IN EFI_HTTP_TOKEN * Token)771 HttpCancel (
772   IN  HTTP_PROTOCOL             *HttpInstance,
773   IN  EFI_HTTP_TOKEN            *Token
774   )
775 {
776   EFI_STATUS                    Status;
777 
778   //
779   // First check the tokens queued by EfiHttpRequest().
780   //
781   Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);
782   if (EFI_ERROR (Status)) {
783     if (Token != NULL) {
784       if (Status == EFI_ABORTED) {
785         return EFI_SUCCESS;
786       }
787     } else {
788       return Status;
789     }
790   }
791 
792   if (!HttpInstance->UseHttps) {
793     //
794     // Then check the tokens queued by EfiHttpResponse(), except for Https.
795     //
796     Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
797     if (EFI_ERROR (Status)) {
798       if (Token != NULL) {
799         if (Status == EFI_ABORTED) {
800           return EFI_SUCCESS;
801         } else {
802           return EFI_NOT_FOUND;
803         }
804       } else {
805         return Status;
806       }
807     }
808   } else {
809     if (!HttpInstance->LocalAddressIsIPv6) {
810       HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
811     } else {
812       HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
813     }
814   }
815 
816   return EFI_SUCCESS;
817 }
818 
819 
820 /**
821   Abort an asynchronous HTTP request or response token.
822 
823   The Cancel() function aborts a pending HTTP request or response transaction. If
824   Token is not NULL and the token is in transmit or receive queues when it is being
825   cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
826   be signaled. If the token is not in one of the queues, which usually means that the
827   asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
828   all asynchronous tokens issued by Request() or Response() will be aborted.
829 
830   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
831   @param[in]  Token               Point to storage containing HTTP request or response
832                                   token.
833 
834   @retval EFI_SUCCESS             Request and Response queues are successfully flushed.
835   @retval EFI_INVALID_PARAMETER   This is NULL.
836   @retval EFI_NOT_STARTED         This instance hasn't been configured.
837   @retval EFI_NOT_FOUND           The asynchronous request or response token is not
838                                   found.
839   @retval EFI_UNSUPPORTED         The implementation does not support this function.
840 
841 **/
842 EFI_STATUS
843 EFIAPI
EfiHttpCancel(IN EFI_HTTP_PROTOCOL * This,IN EFI_HTTP_TOKEN * Token)844 EfiHttpCancel (
845   IN  EFI_HTTP_PROTOCOL         *This,
846   IN  EFI_HTTP_TOKEN            *Token
847   )
848 {
849   HTTP_PROTOCOL                 *HttpInstance;
850 
851   if (This == NULL) {
852     return EFI_INVALID_PARAMETER;
853   }
854 
855   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
856   ASSERT (HttpInstance != NULL);
857 
858   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
859     return EFI_NOT_STARTED;
860   }
861 
862   return HttpCancel (HttpInstance, Token);
863 
864 }
865 
866 /**
867   A callback function to intercept events during message parser.
868 
869   This function will be invoked during HttpParseMessageBody() with various events type. An error
870   return status of the callback function will cause the HttpParseMessageBody() aborted.
871 
872   @param[in]    EventType          Event type of this callback call.
873   @param[in]    Data               A pointer to data buffer.
874   @param[in]    Length             Length in bytes of the Data.
875   @param[in]    Context            Callback context set by HttpInitMsgParser().
876 
877   @retval EFI_SUCCESS              Continue to parser the message body.
878 
879 **/
880 EFI_STATUS
881 EFIAPI
HttpBodyParserCallback(IN HTTP_BODY_PARSE_EVENT EventType,IN CHAR8 * Data,IN UINTN Length,IN VOID * Context)882 HttpBodyParserCallback (
883   IN HTTP_BODY_PARSE_EVENT      EventType,
884   IN CHAR8                      *Data,
885   IN UINTN                      Length,
886   IN VOID                       *Context
887   )
888 {
889   HTTP_TOKEN_WRAP               *Wrap;
890   UINTN                         BodyLength;
891   CHAR8                         *Body;
892 
893   if (EventType != BodyParseEventOnComplete) {
894     return EFI_SUCCESS;
895   }
896 
897   if (Data == NULL || Length != 0 || Context == NULL) {
898     return EFI_SUCCESS;
899   }
900 
901   Wrap = (HTTP_TOKEN_WRAP *) Context;
902   Body = Wrap->HttpToken->Message->Body;
903   BodyLength = Wrap->HttpToken->Message->BodyLength;
904   if (Data < Body + BodyLength) {
905     Wrap->HttpInstance->NextMsg = Data;
906   } else {
907     Wrap->HttpInstance->NextMsg = NULL;
908   }
909 
910 
911   //
912   // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.
913   //
914   FreePool (Wrap);
915 
916   return EFI_SUCCESS;
917 }
918 
919 /**
920   The work function of EfiHttpResponse().
921 
922   @param[in]  Wrap                Pointer to HTTP token's wrap data.
923 
924   @retval EFI_SUCCESS             Allocation succeeded.
925   @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
926   @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or
927                                   the EFI_HTTP_UTILITIES_PROTOCOL is not available.
928 
929 **/
930 EFI_STATUS
HttpResponseWorker(IN HTTP_TOKEN_WRAP * Wrap)931 HttpResponseWorker (
932   IN  HTTP_TOKEN_WRAP           *Wrap
933   )
934 {
935   EFI_STATUS                    Status;
936   EFI_HTTP_MESSAGE              *HttpMsg;
937   CHAR8                         *EndofHeader;
938   CHAR8                         *HttpHeaders;
939   UINTN                         SizeofHeaders;
940   UINTN                         BufferSize;
941   UINTN                         StatusCode;
942   CHAR8                         *Tmp;
943   CHAR8                         *HeaderTmp;
944   CHAR8                         *StatusCodeStr;
945   UINTN                         BodyLen;
946   HTTP_PROTOCOL                 *HttpInstance;
947   EFI_HTTP_TOKEN                *Token;
948   NET_MAP_ITEM                  *Item;
949   HTTP_TOKEN_WRAP               *ValueInItem;
950   UINTN                         HdrLen;
951   NET_FRAGMENT                  Fragment;
952 
953   if (Wrap == NULL || Wrap->HttpInstance == NULL) {
954     return EFI_INVALID_PARAMETER;
955   }
956 
957   HttpInstance = Wrap->HttpInstance;
958   Token = Wrap->HttpToken;
959   HttpMsg = Token->Message;
960 
961   HttpInstance->EndofHeader = NULL;
962   HttpInstance->HttpHeaders = NULL;
963   HttpMsg->Headers          = NULL;
964   HttpHeaders               = NULL;
965   SizeofHeaders             = 0;
966   BufferSize                = 0;
967   EndofHeader               = NULL;
968   ValueInItem               = NULL;
969   Fragment.Len              = 0;
970   Fragment.Bulk             = NULL;
971 
972   if (HttpMsg->Data.Response != NULL) {
973     //
974     // Check whether we have cached header from previous call.
975     //
976     if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
977       //
978       // The data is stored at [NextMsg, CacheBody + CacheLen].
979       //
980       HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
981       HttpHeaders = AllocateZeroPool (HdrLen);
982       if (HttpHeaders == NULL) {
983         Status = EFI_OUT_OF_RESOURCES;
984         goto Error;
985       }
986 
987       CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
988       FreePool (HttpInstance->CacheBody);
989       HttpInstance->CacheBody   = NULL;
990       HttpInstance->NextMsg     = NULL;
991       HttpInstance->CacheOffset = 0;
992       SizeofHeaders = HdrLen;
993       BufferSize = HttpInstance->CacheLen;
994 
995       //
996       // Check whether we cached the whole HTTP headers.
997       //
998       EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR);
999     }
1000 
1001     HttpInstance->EndofHeader = &EndofHeader;
1002     HttpInstance->HttpHeaders = &HttpHeaders;
1003 
1004 
1005     if (HttpInstance->TimeoutEvent == NULL) {
1006       //
1007       // Create TimeoutEvent for response
1008       //
1009       Status = gBS->CreateEvent (
1010                       EVT_TIMER,
1011                       TPL_CALLBACK,
1012                       NULL,
1013                       NULL,
1014                       &HttpInstance->TimeoutEvent
1015                       );
1016       if (EFI_ERROR (Status)) {
1017         goto Error;
1018       }
1019     }
1020 
1021     //
1022     // Start the timer, and wait Timeout seconds to receive the header packet.
1023     //
1024     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
1025     if (EFI_ERROR (Status)) {
1026       goto Error;
1027     }
1028 
1029     Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);
1030 
1031     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
1032 
1033     if (EFI_ERROR (Status)) {
1034       goto Error;
1035     }
1036 
1037     ASSERT (HttpHeaders != NULL);
1038 
1039     //
1040     // Cache the part of body.
1041     //
1042     BodyLen = BufferSize - (EndofHeader - HttpHeaders);
1043     if (BodyLen > 0) {
1044       if (HttpInstance->CacheBody != NULL) {
1045         FreePool (HttpInstance->CacheBody);
1046       }
1047 
1048       HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
1049       if (HttpInstance->CacheBody == NULL) {
1050         Status = EFI_OUT_OF_RESOURCES;
1051         goto Error;
1052       }
1053 
1054       CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
1055       HttpInstance->CacheLen = BodyLen;
1056     }
1057 
1058     //
1059     // Search for Status Code.
1060     //
1061     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
1062     if (StatusCodeStr == NULL) {
1063       Status = EFI_NOT_READY;
1064       goto Error;
1065     }
1066 
1067     StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);
1068 
1069     //
1070     // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
1071     //
1072     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
1073     if (Tmp == NULL) {
1074       Status = EFI_NOT_READY;
1075       goto Error;
1076     }
1077 
1078     //
1079     // We could have response with just a HTTP message and no headers. For Example,
1080     // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
1081     // method. A "\r\n" following Tmp string again would indicate an end. Compare and
1082     // set SizeofHeaders to 0.
1083     //
1084     Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
1085     if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
1086       SizeofHeaders = 0;
1087     } else {
1088       SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
1089     }
1090 
1091     HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
1092     HttpInstance->StatusCode = StatusCode;
1093 
1094     Status = EFI_NOT_READY;
1095     ValueInItem = NULL;
1096 
1097     //
1098     // In cases of PUT/POST, after an initial request-response pair, we would do a
1099     // continuous request without a response call. So, we would not do an insert of
1100     // TxToken. After we have sent the complete file, we will call a response to get
1101     // a final response from server. In such a case, we would not have any TxTokens.
1102     // Hence, check that case before doing a NetMapRemoveHead.
1103     //
1104     if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
1105       NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
1106       if (ValueInItem == NULL)  {
1107         goto Error;
1108       }
1109 
1110       //
1111       // The first Tx Token not transmitted yet, insert back and return error.
1112       //
1113       if (!ValueInItem->TcpWrap.IsTxDone) {
1114         goto Error2;
1115       }
1116     }
1117 
1118     if (SizeofHeaders != 0) {
1119       HeaderTmp = AllocateZeroPool (SizeofHeaders);
1120       if (HeaderTmp == NULL) {
1121         Status = EFI_OUT_OF_RESOURCES;
1122         goto Error2;
1123       }
1124 
1125       CopyMem (HeaderTmp, Tmp, SizeofHeaders);
1126       FreePool (HttpHeaders);
1127       HttpHeaders = HeaderTmp;
1128 
1129       //
1130       // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
1131       //
1132       if (mHttpUtilities == NULL) {
1133         Status = EFI_NOT_READY;
1134         goto Error2;
1135       }
1136 
1137       //
1138       // Parse the HTTP header into array of key/value pairs.
1139       //
1140       Status = mHttpUtilities->Parse (
1141                                  mHttpUtilities,
1142                                  HttpHeaders,
1143                                  SizeofHeaders,
1144                                  &HttpMsg->Headers,
1145                                  &HttpMsg->HeaderCount
1146                                  );
1147       if (EFI_ERROR (Status)) {
1148         goto Error2;
1149       }
1150 
1151       FreePool (HttpHeaders);
1152       HttpHeaders = NULL;
1153 
1154 
1155       //
1156       // Init message-body parser by header information.
1157       //
1158       Status = HttpInitMsgParser (
1159                  HttpInstance->Method,
1160                  HttpMsg->Data.Response->StatusCode,
1161                  HttpMsg->HeaderCount,
1162                  HttpMsg->Headers,
1163                  HttpBodyParserCallback,
1164                  (VOID *) ValueInItem,
1165                  &HttpInstance->MsgParser
1166                  );
1167       if (EFI_ERROR (Status)) {
1168         goto Error2;
1169       }
1170 
1171       //
1172       // Check whether we received a complete HTTP message.
1173       //
1174       if (HttpInstance->CacheBody != NULL) {
1175         Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
1176         if (EFI_ERROR (Status)) {
1177           goto Error2;
1178         }
1179 
1180         if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
1181           //
1182           // Free the MsgParse since we already have a full HTTP message.
1183           //
1184           HttpFreeMsgParser (HttpInstance->MsgParser);
1185           HttpInstance->MsgParser = NULL;
1186         }
1187       }
1188     }
1189 
1190     if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
1191       Status = EFI_SUCCESS;
1192       goto Exit;
1193     }
1194   }
1195 
1196   //
1197   // Receive the response body.
1198   //
1199   BodyLen = 0;
1200 
1201   //
1202   // First check whether we cached some data.
1203   //
1204   if (HttpInstance->CacheBody != NULL) {
1205     //
1206     // Calculate the length of the cached data.
1207     //
1208     if (HttpInstance->NextMsg != NULL) {
1209       //
1210       // We have a cached HTTP message which includes a part of HTTP header of next message.
1211       //
1212       BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);
1213     } else {
1214       BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
1215     }
1216 
1217     if (BodyLen > 0) {
1218       //
1219       // We have some cached data. Just copy the data and return.
1220       //
1221       if (HttpMsg->BodyLength < BodyLen) {
1222         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
1223         HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
1224       } else {
1225         //
1226         // Copy all cached data out.
1227         //
1228         CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
1229         HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
1230         HttpMsg->BodyLength = BodyLen;
1231 
1232         if (HttpInstance->NextMsg == NULL) {
1233           //
1234           // There is no HTTP header of next message. Just free the cache buffer.
1235           //
1236           FreePool (HttpInstance->CacheBody);
1237           HttpInstance->CacheBody   = NULL;
1238           HttpInstance->NextMsg     = NULL;
1239           HttpInstance->CacheOffset = 0;
1240         }
1241       }
1242       //
1243       // Return since we aready received required data.
1244       //
1245       Status = EFI_SUCCESS;
1246       goto Exit;
1247     }
1248 
1249     if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
1250       //
1251       // We received a complete HTTP message, and we don't have more data to return to caller.
1252       //
1253       HttpMsg->BodyLength = 0;
1254       Status = EFI_SUCCESS;
1255       goto Exit;
1256     }
1257   }
1258 
1259   ASSERT (HttpInstance->MsgParser != NULL);
1260 
1261   //
1262   // We still need receive more data when there is no cache data and MsgParser is not NULL;
1263   //
1264   if (!HttpInstance->UseHttps) {
1265     Status = HttpTcpReceiveBody (Wrap, HttpMsg);
1266 
1267     if (EFI_ERROR (Status)) {
1268       goto Error2;
1269     }
1270 
1271   } else {
1272     if (HttpInstance->TimeoutEvent == NULL) {
1273       //
1274       // Create TimeoutEvent for response
1275       //
1276       Status = gBS->CreateEvent (
1277                       EVT_TIMER,
1278                       TPL_CALLBACK,
1279                       NULL,
1280                       NULL,
1281                       &HttpInstance->TimeoutEvent
1282                       );
1283       if (EFI_ERROR (Status)) {
1284         goto Error2;
1285       }
1286     }
1287 
1288     //
1289     // Start the timer, and wait Timeout seconds to receive the body packet.
1290     //
1291     Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
1292     if (EFI_ERROR (Status)) {
1293       goto Error2;
1294     }
1295 
1296     Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
1297 
1298     gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
1299 
1300     if (EFI_ERROR (Status)) {
1301       goto Error2;
1302     }
1303 
1304     //
1305     // Check whether we receive a complete HTTP message.
1306     //
1307     Status = HttpParseMessageBody (
1308                HttpInstance->MsgParser,
1309                (UINTN) Fragment.Len,
1310                (CHAR8 *) Fragment.Bulk
1311                );
1312     if (EFI_ERROR (Status)) {
1313       goto Error2;
1314     }
1315 
1316     if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
1317       //
1318       // Free the MsgParse since we already have a full HTTP message.
1319       //
1320       HttpFreeMsgParser (HttpInstance->MsgParser);
1321       HttpInstance->MsgParser = NULL;
1322     }
1323 
1324     //
1325     // We receive part of header of next HTTP msg.
1326     //
1327     if (HttpInstance->NextMsg != NULL) {
1328       HttpMsg->BodyLength = MIN ((UINTN) (HttpInstance->NextMsg - (CHAR8 *) Fragment.Bulk), HttpMsg->BodyLength);
1329       CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
1330 
1331       HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
1332       if (HttpInstance->CacheLen != 0) {
1333         if (HttpInstance->CacheBody != NULL) {
1334           FreePool (HttpInstance->CacheBody);
1335         }
1336 
1337         HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
1338         if (HttpInstance->CacheBody == NULL) {
1339           Status = EFI_OUT_OF_RESOURCES;
1340           goto Error2;
1341         }
1342 
1343         CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
1344         HttpInstance->CacheOffset = 0;
1345 
1346         HttpInstance->NextMsg = HttpInstance->CacheBody + (UINTN) (HttpInstance->NextMsg - (CHAR8 *) (Fragment.Bulk + HttpMsg->BodyLength));
1347       }
1348     } else {
1349       HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);
1350       CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
1351       HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
1352       if (HttpInstance->CacheLen != 0) {
1353         if (HttpInstance->CacheBody != NULL) {
1354           FreePool (HttpInstance->CacheBody);
1355         }
1356 
1357         HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
1358         if (HttpInstance->CacheBody == NULL) {
1359           Status = EFI_OUT_OF_RESOURCES;
1360           goto Error2;
1361         }
1362 
1363         CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
1364         HttpInstance->CacheOffset = 0;
1365       }
1366     }
1367 
1368     if (Fragment.Bulk != NULL) {
1369       FreePool (Fragment.Bulk);
1370       Fragment.Bulk = NULL;
1371     }
1372 
1373     goto Exit;
1374   }
1375 
1376   return Status;
1377 
1378 Exit:
1379   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
1380   if (Item != NULL) {
1381     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
1382   }
1383 
1384   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
1385     Token->Status = EFI_HTTP_ERROR;
1386   } else {
1387     Token->Status = Status;
1388   }
1389 
1390   gBS->SignalEvent (Token->Event);
1391   HttpCloseTcpRxEvent (Wrap);
1392   FreePool (Wrap);
1393   return Status;
1394 
1395 Error2:
1396   if (ValueInItem != NULL) {
1397     NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
1398   }
1399 
1400 Error:
1401   Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
1402   if (Item != NULL) {
1403     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
1404   }
1405 
1406   if (!HttpInstance->UseHttps) {
1407     HttpTcpTokenCleanup (Wrap);
1408   } else {
1409     FreePool (Wrap);
1410   }
1411 
1412   if (HttpHeaders != NULL) {
1413     FreePool (HttpHeaders);
1414     HttpHeaders = NULL;
1415   }
1416 
1417   if (Fragment.Bulk != NULL) {
1418     FreePool (Fragment.Bulk);
1419     Fragment.Bulk = NULL;
1420   }
1421 
1422   if (HttpMsg->Headers != NULL) {
1423     FreePool (HttpMsg->Headers);
1424     HttpMsg->Headers = NULL;
1425   }
1426 
1427   if (HttpInstance->CacheBody != NULL) {
1428     FreePool (HttpInstance->CacheBody);
1429     HttpInstance->CacheBody = NULL;
1430   }
1431 
1432   if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
1433     Token->Status = EFI_HTTP_ERROR;
1434   } else {
1435     Token->Status = Status;
1436   }
1437 
1438   gBS->SignalEvent (Token->Event);
1439 
1440   return Status;
1441 
1442 }
1443 
1444 
1445 /**
1446   The Response() function queues an HTTP response to this HTTP instance, similar to
1447   Receive() function in the EFI TCP driver. When the HTTP response is received successfully,
1448   or if there is an error, Status in token will be updated and Event will be signaled.
1449 
1450   The HTTP driver will queue a receive token to the underlying TCP instance. When data
1451   is received in the underlying TCP instance, the data will be parsed and Token will
1452   be populated with the response data. If the data received from the remote host
1453   contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
1454   (asynchronously) for more data to be sent from the remote host before signaling
1455   Event in Token.
1456 
1457   It is the responsibility of the caller to allocate a buffer for Body and specify the
1458   size in BodyLength. If the remote host provides a response that contains a content
1459   body, up to BodyLength bytes will be copied from the receive buffer into Body and
1460   BodyLength will be updated with the amount of bytes received and copied to Body. This
1461   allows the client to download a large file in chunks instead of into one contiguous
1462   block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
1463   non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
1464   token to underlying TCP instance. If data arrives in the receive buffer, up to
1465   BodyLength bytes of data will be copied to Body. The HTTP driver will then update
1466   BodyLength with the amount of bytes received and copied to Body.
1467 
1468   If the HTTP driver does not have an open underlying TCP connection with the host
1469   specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
1470   consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
1471   an open TCP connection between client and host.
1472 
1473   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
1474   @param[in]  Token               Pointer to storage containing HTTP response token.
1475 
1476   @retval EFI_SUCCESS             Allocation succeeded.
1477   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been
1478                                   initialized.
1479   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
1480                                   This is NULL.
1481                                   Token is NULL.
1482                                   Token->Message->Headers is NULL.
1483                                   Token->Message is NULL.
1484                                   Token->Message->Body is not NULL,
1485                                   Token->Message->BodyLength is non-zero, and
1486                                   Token->Message->Data is NULL, but a previous call to
1487                                   Response() has not been completed successfully.
1488   @retval EFI_OUT_OF_RESOURCES    Could not allocate enough system resources.
1489   @retval EFI_ACCESS_DENIED       An open TCP connection is not present with the host
1490                                   specified by response URL.
1491 **/
1492 EFI_STATUS
1493 EFIAPI
EfiHttpResponse(IN EFI_HTTP_PROTOCOL * This,IN EFI_HTTP_TOKEN * Token)1494 EfiHttpResponse (
1495   IN  EFI_HTTP_PROTOCOL         *This,
1496   IN  EFI_HTTP_TOKEN            *Token
1497   )
1498 {
1499   EFI_STATUS                    Status;
1500   EFI_HTTP_MESSAGE              *HttpMsg;
1501   HTTP_PROTOCOL                 *HttpInstance;
1502   HTTP_TOKEN_WRAP               *Wrap;
1503 
1504   if ((This == NULL) || (Token == NULL)) {
1505     return EFI_INVALID_PARAMETER;
1506   }
1507 
1508   HttpMsg = Token->Message;
1509   if (HttpMsg == NULL) {
1510     return EFI_INVALID_PARAMETER;
1511   }
1512 
1513   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
1514   ASSERT (HttpInstance != NULL);
1515 
1516   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
1517     return EFI_NOT_STARTED;
1518   }
1519 
1520   //
1521   // Check whether the token already existed.
1522   //
1523   if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {
1524     return EFI_ACCESS_DENIED;
1525   }
1526 
1527   Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
1528   if (Wrap == NULL) {
1529     return EFI_OUT_OF_RESOURCES;
1530   }
1531 
1532   Wrap->HttpInstance = HttpInstance;
1533   Wrap->HttpToken    = Token;
1534 
1535   //
1536   // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
1537   // receive the https response. A special TlsRxToken is used for receiving TLS
1538   // related messages. It should be a blocking response.
1539   //
1540   if (!HttpInstance->UseHttps) {
1541     Status = HttpCreateTcpRxEvent (Wrap);
1542     if (EFI_ERROR (Status)) {
1543       goto Error;
1544     }
1545   }
1546 
1547   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);
1548   if (EFI_ERROR (Status)) {
1549     goto Error;
1550   }
1551 
1552   //
1553   // If already have pending RxTokens, return directly.
1554   //
1555   if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {
1556     return EFI_SUCCESS;
1557   }
1558 
1559   return HttpResponseWorker (Wrap);
1560 
1561 Error:
1562   if (Wrap != NULL) {
1563     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
1564       gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
1565     }
1566 
1567     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
1568       gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
1569     }
1570     FreePool (Wrap);
1571   }
1572 
1573   return Status;
1574 }
1575 
1576 /**
1577   The Poll() function can be used by network drivers and applications to increase the
1578   rate that data packets are moved between the communication devices and the transmit
1579   and receive queues.
1580 
1581   In some systems, the periodic timer event in the managed network driver may not poll
1582   the underlying communications device fast enough to transmit and/or receive all data
1583   packets without missing incoming packets or dropping outgoing packets. Drivers and
1584   applications that are experiencing packet loss should try calling the Poll() function
1585   more often.
1586 
1587   @param[in]  This                Pointer to EFI_HTTP_PROTOCOL instance.
1588 
1589   @retval EFI_SUCCESS             Incoming or outgoing data was processed.
1590   @retval EFI_DEVICE_ERROR        An unexpected system or network error occurred.
1591   @retval EFI_INVALID_PARAMETER   This is NULL.
1592   @retval EFI_NOT_READY           No incoming or outgoing data is processed.
1593   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been started.
1594 
1595 **/
1596 EFI_STATUS
1597 EFIAPI
EfiHttpPoll(IN EFI_HTTP_PROTOCOL * This)1598 EfiHttpPoll (
1599   IN  EFI_HTTP_PROTOCOL         *This
1600   )
1601 {
1602   EFI_STATUS                    Status;
1603   HTTP_PROTOCOL                 *HttpInstance;
1604 
1605   if (This == NULL) {
1606     return EFI_INVALID_PARAMETER;
1607   }
1608 
1609   HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
1610   ASSERT (HttpInstance != NULL);
1611 
1612   if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
1613     return EFI_NOT_STARTED;
1614   }
1615 
1616   if (HttpInstance->LocalAddressIsIPv6) {
1617     if (HttpInstance->Tcp6 == NULL) {
1618       return EFI_NOT_STARTED;
1619     }
1620     Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
1621   } else {
1622     if (HttpInstance->Tcp4 == NULL) {
1623       return EFI_NOT_STARTED;
1624     }
1625     Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
1626   }
1627 
1628   DispatchDpc ();
1629 
1630   return Status;
1631 }
1632