VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c@ 106411

最後變更 在這個檔案從106411是 105670,由 vboxsync 提交於 6 月 前

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette