2 Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
4 Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
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.
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.
17 #include "HttpDriver.h"
19 EFI_HTTP_PROTOCOL mEfiHttpTemplate
= {
29 Returns the operational parameters for the current HTTP child instance.
31 The GetModeData() function is used to read the current mode data (operational
32 parameters) for this HTTP protocol instance.
34 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
35 @param[out] HttpConfigData Point to buffer for operational parameters of this
38 @retval EFI_SUCCESS Operation succeeded.
39 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
41 HttpConfigData is NULL.
42 HttpInstance->LocalAddressIsIPv6 is FALSE and
43 HttpConfigData->IPv4Node is NULL.
44 HttpInstance->LocalAddressIsIPv6 is TRUE and
45 HttpConfigData->IPv6Node is NULL.
46 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
52 IN EFI_HTTP_PROTOCOL
*This
,
53 OUT EFI_HTTP_CONFIG_DATA
*HttpConfigData
56 HTTP_PROTOCOL
*HttpInstance
;
59 // Check input parameters.
61 if ((This
== NULL
) || (HttpConfigData
== NULL
)) {
62 return EFI_INVALID_PARAMETER
;
65 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
66 ASSERT (HttpInstance
!= NULL
);
68 if ((HttpInstance
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv6Node
== NULL
) ||
69 (!HttpInstance
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv4Node
== NULL
)) {
70 return EFI_INVALID_PARAMETER
;
73 if (HttpInstance
->State
< HTTP_STATE_HTTP_CONFIGED
) {
74 return EFI_NOT_STARTED
;
77 HttpConfigData
->HttpVersion
= HttpInstance
->HttpVersion
;
78 HttpConfigData
->TimeOutMillisec
= HttpInstance
->TimeOutMillisec
;
79 HttpConfigData
->LocalAddressIsIPv6
= HttpInstance
->LocalAddressIsIPv6
;
81 if (HttpInstance
->LocalAddressIsIPv6
) {
83 HttpConfigData
->AccessPoint
.IPv6Node
,
84 &HttpInstance
->Ipv6Node
,
85 sizeof (HttpInstance
->Ipv6Node
)
89 HttpConfigData
->AccessPoint
.IPv4Node
,
90 &HttpInstance
->IPv4Node
,
91 sizeof (HttpInstance
->IPv4Node
)
99 Initialize or brutally reset the operational parameters for this EFI HTTP instance.
101 The Configure() function does the following:
102 When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring
103 timeout, local address, port, etc.
104 When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active
105 connections with remote hosts, canceling all asynchronous tokens, and flush request
106 and response buffers without informing the appropriate hosts.
108 No other EFI HTTP function can be executed by this instance until the Configure()
109 function is executed and returns successfully.
111 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
112 @param[in] HttpConfigData Pointer to the configure data to configure the instance.
114 @retval EFI_SUCCESS Operation succeeded.
115 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
117 HttpConfigData is NULL.
118 HttpConfigData->LocalAddressIsIPv6 is FALSE and
119 HttpConfigData->IPv4Node is NULL.
120 HttpConfigData->LocalAddressIsIPv6 is TRUE and
121 HttpConfigData->IPv6Node is NULL.
122 @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling
123 Configure() with NULL to reset it.
124 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
125 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when
126 executing Configure().
127 @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported
128 in the implementation.
133 IN EFI_HTTP_PROTOCOL
*This
,
134 IN EFI_HTTP_CONFIG_DATA
*HttpConfigData
137 HTTP_PROTOCOL
*HttpInstance
;
141 // Check input parameters.
144 HttpConfigData
== NULL
||
145 ((HttpConfigData
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv6Node
== NULL
) ||
146 (!HttpConfigData
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv4Node
== NULL
))) {
147 return EFI_INVALID_PARAMETER
;
150 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
151 ASSERT (HttpInstance
!= NULL
&& HttpInstance
->Service
!= NULL
);
153 if (HttpConfigData
!= NULL
) {
156 // Now configure this HTTP instance.
158 if (HttpInstance
->State
!= HTTP_STATE_UNCONFIGED
) {
159 return EFI_ALREADY_STARTED
;
162 HttpInstance
->HttpVersion
= HttpConfigData
->HttpVersion
;
163 HttpInstance
->TimeOutMillisec
= HttpConfigData
->TimeOutMillisec
;
164 HttpInstance
->LocalAddressIsIPv6
= HttpConfigData
->LocalAddressIsIPv6
;
166 if (HttpConfigData
->LocalAddressIsIPv6
) {
168 &HttpInstance
->Ipv6Node
,
169 HttpConfigData
->AccessPoint
.IPv6Node
,
170 sizeof (HttpInstance
->Ipv6Node
)
174 &HttpInstance
->IPv4Node
,
175 HttpConfigData
->AccessPoint
.IPv4Node
,
176 sizeof (HttpInstance
->IPv4Node
)
183 Status
= HttpInitProtocol (HttpInstance
, HttpInstance
->LocalAddressIsIPv6
);
184 if (EFI_ERROR (Status
)) {
188 HttpInstance
->State
= HTTP_STATE_HTTP_CONFIGED
;
193 // Reset all the resources related to HttpInsance.
195 HttpCleanProtocol (HttpInstance
);
196 HttpInstance
->State
= HTTP_STATE_UNCONFIGED
;
203 The Request() function queues an HTTP request to this HTTP instance.
205 Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent
206 successfully, or if there is an error, Status in token will be updated and Event will
209 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
210 @param[in] Token Pointer to storage containing HTTP request token.
212 @retval EFI_SUCCESS Outgoing data was processed.
213 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
214 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
215 @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue.
216 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
217 @retval EFI_UNSUPPORTED The HTTP method is not supported in current
219 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
222 Token->Message is NULL.
223 Token->Message->Body is not NULL,
224 Token->Message->BodyLength is non-zero, and
225 Token->Message->Data is NULL, but a previous call to
226 Request()has not been completed successfully.
231 IN EFI_HTTP_PROTOCOL
*This
,
232 IN EFI_HTTP_TOKEN
*Token
235 EFI_HTTP_MESSAGE
*HttpMsg
;
236 EFI_HTTP_REQUEST_DATA
*Request
;
241 HTTP_PROTOCOL
*HttpInstance
;
248 HTTP_TOKEN_WRAP
*Wrap
;
250 UINTN RequestMsgSize
;
252 if ((This
== NULL
) || (Token
== NULL
)) {
253 return EFI_INVALID_PARAMETER
;
256 HttpMsg
= Token
->Message
;
257 if ((HttpMsg
== NULL
) || (HttpMsg
->Headers
== NULL
)) {
258 return EFI_INVALID_PARAMETER
;
262 // Current implementation does not support POST/PUT method.
263 // If future version supports these two methods, Request could be NULL for a special case that to send large amounts
264 // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.
267 Request
= HttpMsg
->Data
.Request
;
268 if ((Request
== NULL
) || (Request
->Url
== NULL
)) {
269 return EFI_INVALID_PARAMETER
;
273 // Only support GET and HEAD method in current implementation.
275 if ((Request
->Method
!= HttpMethodGet
) && (Request
->Method
!= HttpMethodHead
)) {
276 return EFI_UNSUPPORTED
;
279 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
280 ASSERT (HttpInstance
!= NULL
);
282 if (HttpInstance
->State
< HTTP_STATE_HTTP_CONFIGED
) {
283 return EFI_NOT_STARTED
;
287 // Check whether the token already existed.
289 if (EFI_ERROR (NetMapIterate (&HttpInstance
->TxTokens
, HttpTokenExist
, Token
))) {
290 return EFI_ACCESS_DENIED
;
298 // Parse the URI of the remote host.
300 Url
= HttpInstance
->Url
;
301 UrlLen
= StrLen (Request
->Url
) + 1;
302 if (UrlLen
> HTTP_URL_BUFFER_LEN
) {
303 Url
= AllocateZeroPool (UrlLen
);
305 return EFI_OUT_OF_RESOURCES
;
307 FreePool (HttpInstance
->Url
);
308 HttpInstance
->Url
= Url
;
312 UnicodeStrToAsciiStr (Request
->Url
, Url
);
314 Status
= HttpParseUrl (Url
, (UINT32
) AsciiStrLen (Url
), FALSE
, &UrlParser
);
315 if (EFI_ERROR (Status
)) {
321 Status
= HttpUrlGetHostName (Url
, UrlParser
, &HostName
);
322 if (EFI_ERROR (Status
)) {
326 Status
= HttpUrlGetPort (Url
, UrlParser
, &RemotePort
);
327 if (EFI_ERROR (Status
)) {
328 RemotePort
= HTTP_DEFAULT_PORT
;
331 // If Configure is TRUE, it indicates the first time to call Request();
332 // If ReConfigure is TRUE, it indicates the request URL is not same
333 // with the previous call to Request();
338 if (HttpInstance
->RemoteHost
== NULL
) {
340 // Request() is called the first time.
344 if ((HttpInstance
->RemotePort
== RemotePort
) &&
345 (AsciiStrCmp (HttpInstance
->RemoteHost
, HostName
) == 0)) {
347 // Host Name and port number of the request URL are the same with previous call to Request().
348 // Check whether previous TCP packet sent out.
350 if (EFI_ERROR (NetMapIterate (&HttpInstance
->TxTokens
, HttpTcpNotReady
, NULL
))) {
352 // Wrap the HTTP token in HTTP_TOKEN_WRAP
354 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
356 Status
= EFI_OUT_OF_RESOURCES
;
360 Wrap
->HttpToken
= Token
;
361 Wrap
->HttpInstance
= HttpInstance
;
363 Status
= HttpCreateTcpTxEvent (Wrap
);
364 if (EFI_ERROR (Status
)) {
368 Status
= NetMapInsertTail (&HttpInstance
->TxTokens
, Token
, Wrap
);
369 if (EFI_ERROR (Status
)) {
373 Wrap
->TcpWrap
.Method
= Request
->Method
;
378 // Queue the HTTP token and return.
383 // Use existing TCP instance to transmit the packet.
390 // Need close existing TCP instance and create a new TCP instance for data transmit.
392 if (HttpInstance
->RemoteHost
!= NULL
) {
393 FreePool (HttpInstance
->RemoteHost
);
394 HttpInstance
->RemoteHost
= NULL
;
395 HttpInstance
->RemotePort
= 0;
402 // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
404 if (!HttpInstance
->LocalAddressIsIPv6
) {
405 Status
= NetLibAsciiStrToIp4 (HostName
, &HttpInstance
->RemoteAddr
);
407 Status
= HttpUrlGetIp6 (Url
, UrlParser
, &HttpInstance
->RemoteIpv6Addr
);
410 if (EFI_ERROR (Status
)) {
411 HostNameStr
= AllocateZeroPool ((AsciiStrLen (HostName
) + 1) * sizeof (CHAR16
));
412 if (HostNameStr
== NULL
) {
413 Status
= EFI_OUT_OF_RESOURCES
;
417 AsciiStrToUnicodeStr (HostName
, HostNameStr
);
418 if (!HttpInstance
->LocalAddressIsIPv6
) {
419 Status
= HttpDns4 (HttpInstance
, HostNameStr
, &HttpInstance
->RemoteAddr
);
421 Status
= HttpDns6 (HttpInstance
, HostNameStr
, &HttpInstance
->RemoteIpv6Addr
);
424 FreePool (HostNameStr
);
425 if (EFI_ERROR (Status
)) {
432 // Save the RemotePort and RemoteHost.
434 ASSERT (HttpInstance
->RemoteHost
== NULL
);
435 HttpInstance
->RemotePort
= RemotePort
;
436 HttpInstance
->RemoteHost
= HostName
;
442 // The request URL is different from previous calls to Request(), close existing TCP instance.
444 if (!HttpInstance
->LocalAddressIsIPv6
) {
445 ASSERT (HttpInstance
->Tcp4
!= NULL
);
447 ASSERT (HttpInstance
->Tcp6
!= NULL
);
449 HttpCloseConnection (HttpInstance
);
450 EfiHttpCancel (This
, NULL
);
454 // Wrap the HTTP token in HTTP_TOKEN_WRAP
456 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
458 Status
= EFI_OUT_OF_RESOURCES
;
462 Wrap
->HttpToken
= Token
;
463 Wrap
->HttpInstance
= HttpInstance
;
464 Wrap
->TcpWrap
.Method
= Request
->Method
;
466 Status
= HttpInitTcp (HttpInstance
, Wrap
, Configure
);
467 if (EFI_ERROR (Status
)) {
473 // For the new HTTP token, create TX TCP token events.
475 Status
= HttpCreateTcpTxEvent (Wrap
);
476 if (EFI_ERROR (Status
)) {
482 // Create request message.
485 if (*FileUrl
!= '/') {
487 // Convert the absolute-URI to the absolute-path
489 while (*FileUrl
!= ':') {
492 if ((*(FileUrl
+1) == '/') && (*(FileUrl
+2) == '/')) {
494 while (*FileUrl
!= '/') {
498 Status
= EFI_INVALID_PARAMETER
;
503 Status
= HttpGenRequestMessage (HttpMsg
, FileUrl
, &RequestMsg
, &RequestMsgSize
);
505 if (EFI_ERROR (Status
)) {
509 Status
= NetMapInsertTail (&HttpInstance
->TxTokens
, Token
, Wrap
);
510 if (EFI_ERROR (Status
)) {
515 // Transmit the request message.
517 Status
= HttpTransmitTcp (
523 if (EFI_ERROR (Status
)) {
529 if (HostName
!= NULL
) {
536 NetMapRemoveTail (&HttpInstance
->TxTokens
, NULL
);
539 if (RequestMsg
!= NULL
) {
540 FreePool (RequestMsg
);
544 HttpCloseConnection (HttpInstance
);
547 HttpCloseTcpConnCloseEvent (HttpInstance
);
548 if (NULL
!= Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
) {
549 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
);
550 Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
= NULL
;
552 if (NULL
!= Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
) {
553 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
);
554 Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
= NULL
;
559 if (HostName
!= NULL
) {
565 if (UrlParser
!= NULL
) {
566 HttpUrlFreeParser (UrlParser
);
574 Cancel a user's Token.
576 @param[in] Map The HTTP instance's token queue.
577 @param[in] Item Object container for one HTTP token and token's wrap.
578 @param[in] Context The user's token to cancel.
580 @retval EFI_SUCCESS Continue to check the next Item.
581 @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
588 IN NET_MAP_ITEM
*Item
,
593 EFI_HTTP_TOKEN
*Token
;
594 HTTP_TOKEN_WRAP
*Wrap
;
595 HTTP_PROTOCOL
*HttpInstance
;
597 Token
= (EFI_HTTP_TOKEN
*) Context
;
600 // Return EFI_SUCCESS to check the next item in the map if
601 // this one doesn't match.
603 if ((Token
!= NULL
) && (Token
!= Item
->Key
)) {
607 Wrap
= (HTTP_TOKEN_WRAP
*) Item
->Value
;
608 ASSERT (Wrap
!= NULL
);
609 HttpInstance
= Wrap
->HttpInstance
;
614 NetMapRemoveItem (Map
, Item
, NULL
);
616 if (!HttpInstance
->LocalAddressIsIPv6
) {
617 if (Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
!= NULL
) {
618 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
);
621 if (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
!= NULL
) {
622 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
);
625 if (Wrap
->TcpWrap
.Rx4Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
!= NULL
) {
626 FreePool (Wrap
->TcpWrap
.Rx4Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
);
630 if (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
!= NULL
) {
631 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
);
634 if (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
!= NULL
) {
635 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
);
638 if (Wrap
->TcpWrap
.Rx6Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
!= NULL
) {
639 FreePool (Wrap
->TcpWrap
.Rx6Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
);
647 // If only one item is to be cancel, return EFI_ABORTED to stop
648 // iterating the map any more.
658 Cancel the user's receive/transmit request. It is the worker function of
659 EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
662 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
663 @param[in] Token The token to cancel. If NULL, all token will be
666 @retval EFI_SUCCESS The token is cancelled.
667 @retval EFI_NOT_FOUND The asynchronous request or response token is not found.
668 @retval Others Other error as indicated.
673 IN HTTP_PROTOCOL
*HttpInstance
,
674 IN EFI_HTTP_TOKEN
*Token
680 // First check the tokens queued by EfiHttpRequest().
682 Status
= NetMapIterate (&HttpInstance
->TxTokens
, HttpCancelTokens
, Token
);
683 if (EFI_ERROR (Status
)) {
685 if (Status
== EFI_ABORTED
) {
694 // Then check the tokens queued by EfiHttpResponse().
696 Status
= NetMapIterate (&HttpInstance
->RxTokens
, HttpCancelTokens
, Token
);
697 if (EFI_ERROR (Status
)) {
699 if (Status
== EFI_ABORTED
) {
702 return EFI_NOT_FOUND
;
714 Abort an asynchronous HTTP request or response token.
716 The Cancel() function aborts a pending HTTP request or response transaction. If
717 Token is not NULL and the token is in transmit or receive queues when it is being
718 cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
719 be signaled. If the token is not in one of the queues, which usually means that the
720 asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
721 all asynchronous tokens issued by Request() or Response() will be aborted.
723 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
724 @param[in] Token Point to storage containing HTTP request or response
727 @retval EFI_SUCCESS Request and Response queues are successfully flushed.
728 @retval EFI_INVALID_PARAMETER This is NULL.
729 @retval EFI_NOT_STARTED This instance hasn't been configured.
730 @retval EFI_NOT_FOUND The asynchronous request or response token is not
732 @retval EFI_UNSUPPORTED The implementation does not support this function.
738 IN EFI_HTTP_PROTOCOL
*This
,
739 IN EFI_HTTP_TOKEN
*Token
742 HTTP_PROTOCOL
*HttpInstance
;
745 return EFI_INVALID_PARAMETER
;
748 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
749 ASSERT (HttpInstance
!= NULL
);
751 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
) {
752 return EFI_NOT_STARTED
;
755 return HttpCancel (HttpInstance
, Token
);
760 A callback function to intercept events during message parser.
762 This function will be invoked during HttpParseMessageBody() with various events type. An error
763 return status of the callback function will cause the HttpParseMessageBody() aborted.
765 @param[in] EventType Event type of this callback call.
766 @param[in] Data A pointer to data buffer.
767 @param[in] Length Length in bytes of the Data.
768 @param[in] Context Callback context set by HttpInitMsgParser().
770 @retval EFI_SUCCESS Continue to parser the message body.
775 HttpBodyParserCallback (
776 IN HTTP_BODY_PARSE_EVENT EventType
,
782 HTTP_TOKEN_WRAP
*Wrap
;
786 if (EventType
!= BodyParseEventOnComplete
) {
790 if (Data
== NULL
|| Length
!= 0 || Context
== NULL
) {
794 Wrap
= (HTTP_TOKEN_WRAP
*) Context
;
795 Body
= Wrap
->HttpToken
->Message
->Body
;
796 BodyLength
= Wrap
->HttpToken
->Message
->BodyLength
;
797 if (Data
< Body
+ BodyLength
) {
798 Wrap
->HttpInstance
->NextMsg
= Data
;
800 Wrap
->HttpInstance
->NextMsg
= NULL
;
805 // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.
813 The work function of EfiHttpResponse().
815 @param[in] Wrap Pointer to HTTP token's wrap data.
817 @retval EFI_SUCCESS Allocation succeeded.
818 @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources.
819 @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or
820 the EFI_HTTP_UTILITIES_PROTOCOL is not available.
825 IN HTTP_TOKEN_WRAP
*Wrap
829 EFI_HTTP_MESSAGE
*HttpMsg
;
837 CHAR8
*StatusCodeStr
;
839 HTTP_PROTOCOL
*HttpInstance
;
840 EFI_HTTP_TOKEN
*Token
;
842 HTTP_TOKEN_WRAP
*ValueInItem
;
845 if (Wrap
== NULL
|| Wrap
->HttpInstance
== NULL
) {
846 return EFI_INVALID_PARAMETER
;
849 HttpInstance
= Wrap
->HttpInstance
;
850 Token
= Wrap
->HttpToken
;
851 HttpMsg
= Token
->Message
;
853 HttpInstance
->EndofHeader
= NULL
;
854 HttpInstance
->HttpHeaders
= NULL
;
855 HttpMsg
->Headers
= NULL
;
861 if (HttpMsg
->Data
.Response
!= NULL
) {
863 // Need receive the HTTP headers, prepare buffer.
865 Status
= HttpCreateTcpRxEventForHeader (HttpInstance
);
866 if (EFI_ERROR (Status
)) {
871 // Check whether we have cached header from previous call.
873 if ((HttpInstance
->CacheBody
!= NULL
) && (HttpInstance
->NextMsg
!= NULL
)) {
875 // The data is stored at [NextMsg, CacheBody + CacheLen].
877 HdrLen
= HttpInstance
->CacheBody
+ HttpInstance
->CacheLen
- HttpInstance
->NextMsg
;
878 HttpHeaders
= AllocateZeroPool (HdrLen
);
879 if (HttpHeaders
== NULL
) {
880 Status
= EFI_OUT_OF_RESOURCES
;
884 CopyMem (HttpHeaders
, HttpInstance
->NextMsg
, HdrLen
);
885 FreePool (HttpInstance
->CacheBody
);
886 HttpInstance
->CacheBody
= NULL
;
887 HttpInstance
->NextMsg
= NULL
;
888 HttpInstance
->CacheOffset
= 0;
889 SizeofHeaders
= HdrLen
;
890 BufferSize
= HttpInstance
->CacheLen
;
893 // Check whether we cached the whole HTTP headers.
895 EndofHeader
= AsciiStrStr (HttpHeaders
, HTTP_END_OF_HDR_STR
);
898 HttpInstance
->EndofHeader
= &EndofHeader
;
899 HttpInstance
->HttpHeaders
= &HttpHeaders
;
902 if (HttpInstance
->TimeoutEvent
== NULL
) {
904 // Create TimeoutEvent for response
906 Status
= gBS
->CreateEvent (
911 &HttpInstance
->TimeoutEvent
913 if (EFI_ERROR (Status
)) {
919 // Start the timer, and wait Timeout seconds to receive the header packet.
921 Status
= gBS
->SetTimer (HttpInstance
->TimeoutEvent
, TimerRelative
, HTTP_RESPONSE_TIMEOUT
* TICKS_PER_SECOND
);
922 if (EFI_ERROR (Status
)) {
926 Status
= HttpTcpReceiveHeader (HttpInstance
, &SizeofHeaders
, &BufferSize
, HttpInstance
->TimeoutEvent
);
928 gBS
->SetTimer (HttpInstance
->TimeoutEvent
, TimerCancel
, 0);
930 if (EFI_ERROR (Status
)) {
934 ASSERT (HttpHeaders
!= NULL
);
937 // Cache the part of body.
939 BodyLen
= BufferSize
- (EndofHeader
- HttpHeaders
);
941 if (HttpInstance
->CacheBody
!= NULL
) {
942 FreePool (HttpInstance
->CacheBody
);
945 HttpInstance
->CacheBody
= AllocateZeroPool (BodyLen
);
946 if (HttpInstance
->CacheBody
== NULL
) {
947 Status
= EFI_OUT_OF_RESOURCES
;
951 CopyMem (HttpInstance
->CacheBody
, EndofHeader
, BodyLen
);
952 HttpInstance
->CacheLen
= BodyLen
;
956 // Search for Status Code.
958 StatusCodeStr
= HttpHeaders
+ AsciiStrLen (HTTP_VERSION_STR
) + 1;
959 if (StatusCodeStr
== NULL
) {
963 StatusCode
= AsciiStrDecimalToUintn (StatusCodeStr
);
966 // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
968 Tmp
= AsciiStrStr (HttpHeaders
, HTTP_CRLF_STR
);
973 Tmp
= Tmp
+ AsciiStrLen (HTTP_CRLF_STR
);
974 SizeofHeaders
= SizeofHeaders
- (Tmp
- HttpHeaders
);
975 HeaderTmp
= AllocateZeroPool (SizeofHeaders
);
976 if (HeaderTmp
== NULL
) {
980 CopyMem (HeaderTmp
, Tmp
, SizeofHeaders
);
981 FreePool (HttpHeaders
);
982 HttpHeaders
= HeaderTmp
;
985 // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
987 if (mHttpUtilities
== NULL
) {
988 Status
= EFI_NOT_READY
;
993 // Parse the HTTP header into array of key/value pairs.
995 Status
= mHttpUtilities
->Parse (
1000 &HttpMsg
->HeaderCount
1002 if (EFI_ERROR (Status
)) {
1006 FreePool (HttpHeaders
);
1009 HttpMsg
->Data
.Response
->StatusCode
= HttpMappingToStatusCode (StatusCode
);
1010 HttpInstance
->StatusCode
= StatusCode
;
1012 // Init message-body parser by header information.
1014 Status
= EFI_NOT_READY
;
1016 NetMapRemoveHead (&HttpInstance
->TxTokens
, (VOID
**) &ValueInItem
);
1017 if (ValueInItem
== NULL
) {
1022 // The first Tx Token not transmitted yet, insert back and return error.
1024 if (!ValueInItem
->TcpWrap
.IsTxDone
) {
1028 Status
= HttpInitMsgParser (
1029 ValueInItem
->TcpWrap
.Method
,
1030 HttpMsg
->Data
.Response
->StatusCode
,
1031 HttpMsg
->HeaderCount
,
1033 HttpBodyParserCallback
,
1034 (VOID
*) ValueInItem
,
1035 &HttpInstance
->MsgParser
1037 if (EFI_ERROR (Status
)) {
1042 // Check whether we received a complete HTTP message.
1044 if (HttpInstance
->CacheBody
!= NULL
) {
1045 Status
= HttpParseMessageBody (HttpInstance
->MsgParser
, HttpInstance
->CacheLen
, HttpInstance
->CacheBody
);
1046 if (EFI_ERROR (Status
)) {
1050 if (HttpIsMessageComplete (HttpInstance
->MsgParser
)) {
1052 // Free the MsgParse since we already have a full HTTP message.
1054 HttpFreeMsgParser (HttpInstance
->MsgParser
);
1055 HttpInstance
->MsgParser
= NULL
;
1059 if ((HttpMsg
->Body
== NULL
) || (HttpMsg
->BodyLength
== 0)) {
1060 Status
= EFI_SUCCESS
;
1066 // Receive the response body.
1071 // First check whether we cached some data.
1073 if (HttpInstance
->CacheBody
!= NULL
) {
1075 // Calculate the length of the cached data.
1077 if (HttpInstance
->NextMsg
!= NULL
) {
1079 // We have a cached HTTP message which includes a part of HTTP header of next message.
1081 BodyLen
= HttpInstance
->NextMsg
- (HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
);
1083 BodyLen
= HttpInstance
->CacheLen
- HttpInstance
->CacheOffset
;
1088 // We have some cached data. Just copy the data and return.
1090 if (HttpMsg
->BodyLength
< BodyLen
) {
1091 CopyMem (HttpMsg
->Body
, HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
, HttpMsg
->BodyLength
);
1092 HttpInstance
->CacheOffset
= HttpInstance
->CacheOffset
+ HttpMsg
->BodyLength
;
1095 // Copy all cached data out.
1097 CopyMem (HttpMsg
->Body
, HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
, BodyLen
);
1098 HttpInstance
->CacheOffset
= BodyLen
+ HttpInstance
->CacheOffset
;
1099 HttpMsg
->BodyLength
= BodyLen
;
1101 if (HttpInstance
->NextMsg
== NULL
) {
1103 // There is no HTTP header of next message. Just free the cache buffer.
1105 FreePool (HttpInstance
->CacheBody
);
1106 HttpInstance
->CacheBody
= NULL
;
1107 HttpInstance
->NextMsg
= NULL
;
1108 HttpInstance
->CacheOffset
= 0;
1112 // Return since we aready received required data.
1114 Status
= EFI_SUCCESS
;
1118 if (BodyLen
== 0 && HttpInstance
->MsgParser
== NULL
) {
1120 // We received a complete HTTP message, and we don't have more data to return to caller.
1122 HttpMsg
->BodyLength
= 0;
1123 Status
= EFI_SUCCESS
;
1128 ASSERT (HttpInstance
->MsgParser
!= NULL
);
1130 if (HttpInstance
->TimeoutEvent
== NULL
) {
1132 // Create TimeoutEvent for response
1134 Status
= gBS
->CreateEvent (
1139 &HttpInstance
->TimeoutEvent
1141 if (EFI_ERROR (Status
)) {
1147 // Start the timer, and wait Timeout seconds to receive the body packet.
1149 Status
= gBS
->SetTimer (HttpInstance
->TimeoutEvent
, TimerRelative
, HTTP_RESPONSE_TIMEOUT
* TICKS_PER_SECOND
);
1150 if (EFI_ERROR (Status
)) {
1155 // We still need receive more data when there is no cache data and MsgParser is not NULL;
1157 Status
= HttpTcpReceiveBody (Wrap
, HttpMsg
, HttpInstance
->TimeoutEvent
);
1159 gBS
->SetTimer (HttpInstance
->TimeoutEvent
, TimerCancel
, 0);
1161 if (EFI_ERROR (Status
)) {
1168 Item
= NetMapFindKey (&Wrap
->HttpInstance
->RxTokens
, Wrap
->HttpToken
);
1170 NetMapRemoveItem (&Wrap
->HttpInstance
->RxTokens
, Item
, NULL
);
1173 if (HttpInstance
->StatusCode
>= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE
) {
1174 Token
->Status
= EFI_HTTP_ERROR
;
1176 Token
->Status
= Status
;
1179 gBS
->SignalEvent (Token
->Event
);
1180 HttpCloseTcpRxEvent (Wrap
);
1185 NetMapInsertHead (&HttpInstance
->TxTokens
, ValueInItem
->HttpToken
, ValueInItem
);
1188 HttpTcpTokenCleanup (Wrap
);
1190 if (HttpHeaders
!= NULL
) {
1191 FreePool (HttpHeaders
);
1194 if (HttpMsg
->Headers
!= NULL
) {
1195 FreePool (HttpMsg
->Headers
);
1198 if (HttpInstance
->CacheBody
!= NULL
) {
1199 FreePool (HttpInstance
->CacheBody
);
1200 HttpInstance
->CacheBody
= NULL
;
1203 if (HttpInstance
->StatusCode
>= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE
) {
1204 Token
->Status
= EFI_HTTP_ERROR
;
1206 Token
->Status
= Status
;
1209 gBS
->SignalEvent (Token
->Event
);
1217 The Response() function queues an HTTP response to this HTTP instance, similar to
1218 Receive() function in the EFI TCP driver. When the HTTP response is received successfully,
1219 or if there is an error, Status in token will be updated and Event will be signaled.
1221 The HTTP driver will queue a receive token to the underlying TCP instance. When data
1222 is received in the underlying TCP instance, the data will be parsed and Token will
1223 be populated with the response data. If the data received from the remote host
1224 contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
1225 (asynchronously) for more data to be sent from the remote host before signaling
1228 It is the responsibility of the caller to allocate a buffer for Body and specify the
1229 size in BodyLength. If the remote host provides a response that contains a content
1230 body, up to BodyLength bytes will be copied from the receive buffer into Body and
1231 BodyLength will be updated with the amount of bytes received and copied to Body. This
1232 allows the client to download a large file in chunks instead of into one contiguous
1233 block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
1234 non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
1235 token to underlying TCP instance. If data arrives in the receive buffer, up to
1236 BodyLength bytes of data will be copied to Body. The HTTP driver will then update
1237 BodyLength with the amount of bytes received and copied to Body.
1239 If the HTTP driver does not have an open underlying TCP connection with the host
1240 specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
1241 consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
1242 an open TCP connection between client and host.
1244 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
1245 @param[in] Token Pointer to storage containing HTTP response token.
1247 @retval EFI_SUCCESS Allocation succeeded.
1248 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been
1250 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
1253 Token->Message->Headers is NULL.
1254 Token->Message is NULL.
1255 Token->Message->Body is not NULL,
1256 Token->Message->BodyLength is non-zero, and
1257 Token->Message->Data is NULL, but a previous call to
1258 Response() has not been completed successfully.
1259 @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
1260 @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host
1261 specified by response URL.
1266 IN EFI_HTTP_PROTOCOL
*This
,
1267 IN EFI_HTTP_TOKEN
*Token
1271 EFI_HTTP_MESSAGE
*HttpMsg
;
1272 HTTP_PROTOCOL
*HttpInstance
;
1273 HTTP_TOKEN_WRAP
*Wrap
;
1275 if ((This
== NULL
) || (Token
== NULL
)) {
1276 return EFI_INVALID_PARAMETER
;
1279 HttpMsg
= Token
->Message
;
1280 if (HttpMsg
== NULL
) {
1281 return EFI_INVALID_PARAMETER
;
1284 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
1285 ASSERT (HttpInstance
!= NULL
);
1287 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
) {
1288 return EFI_NOT_STARTED
;
1292 // Check whether the token already existed.
1294 if (EFI_ERROR (NetMapIterate (&HttpInstance
->RxTokens
, HttpTokenExist
, Token
))) {
1295 return EFI_ACCESS_DENIED
;
1298 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
1300 return EFI_OUT_OF_RESOURCES
;
1303 Wrap
->HttpInstance
= HttpInstance
;
1304 Wrap
->HttpToken
= Token
;
1306 Status
= HttpCreateTcpRxEvent (Wrap
);
1307 if (EFI_ERROR (Status
)) {
1311 Status
= NetMapInsertTail (&HttpInstance
->RxTokens
, Token
, Wrap
);
1312 if (EFI_ERROR (Status
)) {
1317 // If already have pending RxTokens, return directly.
1319 if (NetMapGetCount (&HttpInstance
->RxTokens
) > 1) {
1323 return HttpResponseWorker (Wrap
);
1327 if (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
!= NULL
) {
1328 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
);
1331 if (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
!= NULL
) {
1332 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
);
1341 The Poll() function can be used by network drivers and applications to increase the
1342 rate that data packets are moved between the communication devices and the transmit
1345 In some systems, the periodic timer event in the managed network driver may not poll
1346 the underlying communications device fast enough to transmit and/or receive all data
1347 packets without missing incoming packets or dropping outgoing packets. Drivers and
1348 applications that are experiencing packet loss should try calling the Poll() function
1351 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
1353 @retval EFI_SUCCESS Incoming or outgoing data was processed.
1354 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
1355 @retval EFI_INVALID_PARAMETER This is NULL.
1356 @retval EFI_NOT_READY No incoming or outgoing data is processed.
1357 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
1363 IN EFI_HTTP_PROTOCOL
*This
1367 HTTP_PROTOCOL
*HttpInstance
;
1370 return EFI_INVALID_PARAMETER
;
1373 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
1374 ASSERT (HttpInstance
!= NULL
);
1376 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
) {
1377 return EFI_NOT_STARTED
;
1380 if (HttpInstance
->LocalAddressIsIPv6
) {
1381 if (HttpInstance
->Tcp6
== NULL
) {
1382 return EFI_NOT_STARTED
;
1384 Status
= HttpInstance
->Tcp6
->Poll (HttpInstance
->Tcp6
);
1386 if (HttpInstance
->Tcp4
== NULL
) {
1387 return EFI_NOT_STARTED
;
1389 Status
= HttpInstance
->Tcp4
->Poll (HttpInstance
->Tcp4
);