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