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