2 Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015 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 HttpConfigData->AccessPoint is NULL.
43 @retval EFI_NOT_STARTED The HTTP instance is not configured.
49 IN EFI_HTTP_PROTOCOL
*This
,
50 OUT EFI_HTTP_CONFIG_DATA
*HttpConfigData
53 HTTP_PROTOCOL
*HttpInstance
;
54 EFI_HTTPv4_ACCESS_POINT
*Http4AccessPoint
;
55 EFI_HTTPv6_ACCESS_POINT
*Http6AccessPoint
;
57 if ((This
== NULL
) || (HttpConfigData
== NULL
)) {
58 return EFI_INVALID_PARAMETER
;
61 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
62 ASSERT (HttpInstance
!= NULL
);
64 if (HttpInstance
->State
< HTTP_STATE_HTTP_CONFIGED
) {
65 return EFI_NOT_STARTED
;
68 HttpConfigData
->HttpVersion
= HttpInstance
->HttpVersion
;
69 HttpConfigData
->TimeOutMillisec
= HttpInstance
->TimeOutMillisec
;
70 HttpConfigData
->LocalAddressIsIPv6
= HttpInstance
->LocalAddressIsIPv6
;
72 if (HttpInstance
->LocalAddressIsIPv6
) {
73 Http6AccessPoint
= AllocateZeroPool (sizeof (EFI_HTTPv6_ACCESS_POINT
));
76 &HttpInstance
->Ipv6Node
,
77 sizeof (HttpInstance
->Ipv6Node
)
79 HttpConfigData
->AccessPoint
.IPv6Node
= Http6AccessPoint
;
81 Http4AccessPoint
= AllocateZeroPool (sizeof (EFI_HTTPv4_ACCESS_POINT
));
84 &HttpInstance
->IPv4Node
,
85 sizeof (HttpInstance
->IPv4Node
)
87 HttpConfigData
->AccessPoint
.IPv4Node
= Http4AccessPoint
;
94 Initialize or brutally reset the operational parameters for this EFI HTTP instance.
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.
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.
106 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
107 @param[in] HttpConfigData Pointer to the configure data to configure the instance.
109 @retval EFI_SUCCESS Operation succeeded.
110 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
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.
127 IN EFI_HTTP_PROTOCOL
*This
,
128 IN EFI_HTTP_CONFIG_DATA
*HttpConfigData
131 HTTP_PROTOCOL
*HttpInstance
;
135 // Check input parameters.
138 (HttpConfigData
!= NULL
&& ((HttpConfigData
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv6Node
== NULL
) ||
139 (!HttpConfigData
->LocalAddressIsIPv6
&& HttpConfigData
->AccessPoint
.IPv4Node
== NULL
)))) {
140 return EFI_INVALID_PARAMETER
;
143 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
144 ASSERT (HttpInstance
!= NULL
&& HttpInstance
->Service
!= NULL
);
146 if (HttpConfigData
!= NULL
) {
149 // Now configure this HTTP instance.
151 if (HttpInstance
->State
!= HTTP_STATE_UNCONFIGED
) {
152 return EFI_ALREADY_STARTED
;
155 HttpInstance
->HttpVersion
= HttpConfigData
->HttpVersion
;
156 HttpInstance
->TimeOutMillisec
= HttpConfigData
->TimeOutMillisec
;
157 HttpInstance
->LocalAddressIsIPv6
= HttpConfigData
->LocalAddressIsIPv6
;
159 if (HttpConfigData
->LocalAddressIsIPv6
) {
161 &HttpInstance
->Ipv6Node
,
162 HttpConfigData
->AccessPoint
.IPv6Node
,
163 sizeof (HttpInstance
->Ipv6Node
)
167 &HttpInstance
->IPv4Node
,
168 HttpConfigData
->AccessPoint
.IPv4Node
,
169 sizeof (HttpInstance
->IPv4Node
)
175 Status
= HttpInitProtocol (HttpInstance
, HttpInstance
->LocalAddressIsIPv6
);
176 if (EFI_ERROR (Status
)) {
180 HttpInstance
->State
= HTTP_STATE_HTTP_CONFIGED
;
185 // Reset all the resources related to HttpInsance.
187 HttpCleanProtocol (HttpInstance
);
188 HttpInstance
->State
= HTTP_STATE_UNCONFIGED
;
195 The Request() function queues an HTTP request to this HTTP instance.
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
201 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
202 @param[in] Token Pointer to storage containing HTTP request token.
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
211 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
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.
222 IN EFI_HTTP_PROTOCOL
*This
,
223 IN EFI_HTTP_TOKEN
*Token
226 EFI_HTTP_MESSAGE
*HttpMsg
;
227 EFI_HTTP_REQUEST_DATA
*Request
;
232 HTTP_PROTOCOL
*HttpInstance
;
239 HTTP_TOKEN_WRAP
*Wrap
;
242 if ((This
== NULL
) || (Token
== NULL
)) {
243 return EFI_INVALID_PARAMETER
;
246 HttpMsg
= Token
->Message
;
247 if ((HttpMsg
== NULL
) || (HttpMsg
->Headers
== NULL
)) {
248 return EFI_INVALID_PARAMETER
;
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.
257 Request
= HttpMsg
->Data
.Request
;
258 if ((Request
== NULL
) || (Request
->Url
== NULL
)) {
259 return EFI_INVALID_PARAMETER
;
263 // Only support GET and HEAD method in current implementation.
265 if ((Request
->Method
!= HttpMethodGet
) && (Request
->Method
!= HttpMethodHead
)) {
266 return EFI_UNSUPPORTED
;
269 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
270 ASSERT (HttpInstance
!= NULL
);
272 if (HttpInstance
->State
< HTTP_STATE_HTTP_CONFIGED
) {
273 return EFI_NOT_STARTED
;
277 // Check whether the token already existed.
279 if (EFI_ERROR (NetMapIterate (&HttpInstance
->TxTokens
, HttpTokenExist
, Token
))) {
280 return EFI_ACCESS_DENIED
;
288 // Parse the URI of the remote host.
290 Url
= HttpInstance
->Url
;
291 UrlLen
= StrLen (Request
->Url
) + 1;
292 if (UrlLen
> HTTP_URL_BUFFER_LEN
) {
293 Url
= AllocateZeroPool (UrlLen
);
295 return EFI_OUT_OF_RESOURCES
;
297 FreePool (HttpInstance
->Url
);
298 HttpInstance
->Url
= Url
;
302 UnicodeStrToAsciiStr (Request
->Url
, Url
);
304 Status
= HttpParseUrl (Url
, (UINT32
) AsciiStrLen (Url
), FALSE
, &UrlParser
);
305 if (EFI_ERROR (Status
)) {
311 Status
= HttpUrlGetHostName (Url
, UrlParser
, &HostName
);
312 if (EFI_ERROR (Status
)) {
316 Status
= HttpUrlGetPort (Url
, UrlParser
, &RemotePort
);
317 if (EFI_ERROR (Status
)) {
318 RemotePort
= HTTP_DEFAULT_PORT
;
324 if (HttpInstance
->RemoteHost
== NULL
) {
326 // Request() is called the first time.
330 if ((HttpInstance
->RemotePort
== RemotePort
) &&
331 (AsciiStrCmp (HttpInstance
->RemoteHost
, HostName
) == 0)) {
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.
336 if (EFI_ERROR (NetMapIterate (&HttpInstance
->TxTokens
, HttpTcpNotReady
, NULL
))) {
338 // Wrap the HTTP token in HTTP_TOKEN_WRAP
340 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
342 Status
= EFI_OUT_OF_RESOURCES
;
346 Wrap
->HttpToken
= Token
;
347 Wrap
->HttpInstance
= HttpInstance
;
349 Status
= HttpCreateTcpTxEvent (Wrap
);
350 if (EFI_ERROR (Status
)) {
354 Status
= NetMapInsertTail (&HttpInstance
->TxTokens
, Token
, Wrap
);
355 if (EFI_ERROR (Status
)) {
359 Wrap
->TcpWrap
.Method
= Request
->Method
;
364 // Queue the HTTP token and return.
369 // Use existing TCP instance to transmit the packet.
376 // Need close existing TCP instance and create a new TCP instance for data transmit.
378 if (HttpInstance
->RemoteHost
!= NULL
) {
379 FreePool (HttpInstance
->RemoteHost
);
380 HttpInstance
->RemoteHost
= NULL
;
381 HttpInstance
->RemotePort
= 0;
388 // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
390 if (!HttpInstance
->LocalAddressIsIPv6
) {
391 Status
= NetLibAsciiStrToIp4 (HostName
, &HttpInstance
->RemoteAddr
);
393 Status
= NetLibAsciiStrToIp6 (HostName
, &HttpInstance
->RemoteIpv6Addr
);
396 if (EFI_ERROR (Status
)) {
397 HostNameStr
= AllocateZeroPool ((AsciiStrLen (HostName
) + 1) * sizeof (CHAR16
));
398 if (HostNameStr
== NULL
) {
399 Status
= EFI_OUT_OF_RESOURCES
;
403 AsciiStrToUnicodeStr (HostName
, HostNameStr
);
404 if (!HttpInstance
->LocalAddressIsIPv6
) {
405 Status
= HttpDns4 (HttpInstance
, HostNameStr
, &HttpInstance
->RemoteAddr
);
407 Status
= HttpDns6 (HttpInstance
, HostNameStr
, &HttpInstance
->RemoteIpv6Addr
);
410 FreePool (HostNameStr
);
411 if (EFI_ERROR (Status
)) {
418 // Save the RemotePort and RemoteHost.
420 ASSERT (HttpInstance
->RemoteHost
== NULL
);
421 HttpInstance
->RemotePort
= RemotePort
;
422 HttpInstance
->RemoteHost
= HostName
;
428 // The request URL is different from previous calls to Request(), close existing TCP instance.
430 ASSERT (HttpInstance
->Tcp4
!= NULL
&&HttpInstance
->Tcp6
!= NULL
);
431 HttpCloseConnection (HttpInstance
);
432 EfiHttpCancel (This
, NULL
);
436 // Wrap the HTTP token in HTTP_TOKEN_WRAP
438 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
440 Status
= EFI_OUT_OF_RESOURCES
;
444 Wrap
->HttpToken
= Token
;
445 Wrap
->HttpInstance
= HttpInstance
;
446 Wrap
->TcpWrap
.Method
= Request
->Method
;
449 Status
= HttpInitTcp (HttpInstance
, Wrap
);
450 if (EFI_ERROR (Status
)) {
456 // For the new HTTP token, create TX TCP token events.
458 Status
= HttpCreateTcpTxEvent (Wrap
);
459 if (EFI_ERROR (Status
)) {
465 // Create request message.
468 if (*FileUrl
!= '/') {
470 // Convert the absolute-URI to the absolute-path
472 while (*FileUrl
!= ':') {
475 if ((*(FileUrl
+1) == '/') && (*(FileUrl
+2) == '/')) {
477 while (*FileUrl
!= '/') {
481 Status
= EFI_INVALID_PARAMETER
;
485 RequestStr
= HttpGenRequestString (HttpInstance
, HttpMsg
, FileUrl
);
486 if (RequestStr
== NULL
) {
487 Status
= EFI_OUT_OF_RESOURCES
;
491 Status
= NetMapInsertTail (&HttpInstance
->TxTokens
, Token
, Wrap
);
492 if (EFI_ERROR (Status
)) {
497 // Transmit the request message.
499 Status
= HttpTransmitTcp (
503 AsciiStrLen (RequestStr
)
505 if (EFI_ERROR (Status
)) {
511 if (HostName
!= NULL
) {
518 NetMapRemoveTail (&HttpInstance
->TxTokens
, NULL
);
521 if (RequestStr
!= NULL
) {
522 FreePool (RequestStr
);
526 HttpCloseConnection (HttpInstance
);
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
;
534 if (NULL
!= Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
) {
535 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
);
536 Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
= NULL
;
541 if (HostName
!= NULL
) {
547 if (UrlParser
!= NULL
) {
548 HttpUrlFreeParser (UrlParser
);
556 Cancel a user's Token.
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.
562 @retval EFI_SUCCESS Continue to check the next Item.
563 @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
570 IN NET_MAP_ITEM
*Item
,
575 EFI_HTTP_TOKEN
*Token
;
576 HTTP_TOKEN_WRAP
*Wrap
;
577 HTTP_PROTOCOL
*HttpInstance
;
579 Token
= (EFI_HTTP_TOKEN
*) Context
;
582 // Return EFI_SUCCESS to check the next item in the map if
583 // this one doesn't match.
585 if ((Token
!= NULL
) && (Token
!= Item
->Key
)) {
589 Wrap
= (HTTP_TOKEN_WRAP
*) Item
->Value
;
590 ASSERT (Wrap
!= NULL
);
591 HttpInstance
= Wrap
->HttpInstance
;
596 NetMapRemoveItem (Map
, Item
, NULL
);
598 if (!HttpInstance
->LocalAddressIsIPv6
) {
599 if (Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
!= NULL
) {
600 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx4Token
.CompletionToken
.Event
);
603 if (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
!= NULL
) {
604 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
);
607 if (Wrap
->TcpWrap
.Rx4Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
!= NULL
) {
608 FreePool (Wrap
->TcpWrap
.Rx4Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
);
612 if (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
!= NULL
) {
613 gBS
->CloseEvent (Wrap
->TcpWrap
.Tx6Token
.CompletionToken
.Event
);
616 if (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
!= NULL
) {
617 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
);
620 if (Wrap
->TcpWrap
.Rx6Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
!= NULL
) {
621 FreePool (Wrap
->TcpWrap
.Rx6Token
.Packet
.RxData
->FragmentTable
[0].FragmentBuffer
);
629 // If only one item is to be cancel, return EFI_ABORTED to stop
630 // iterating the map any more.
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
644 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
645 @param[in] Token The token to cancel. If NULL, all token will be
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.
655 IN HTTP_PROTOCOL
*HttpInstance
,
656 IN EFI_HTTP_TOKEN
*Token
662 // First check the tokens queued by EfiHttpRequest().
664 Status
= NetMapIterate (&HttpInstance
->TxTokens
, HttpCancelTokens
, Token
);
665 if (EFI_ERROR (Status
)) {
667 if (Status
== EFI_ABORTED
) {
676 // Then check the tokens queued by EfiHttpResponse().
678 Status
= NetMapIterate (&HttpInstance
->RxTokens
, HttpCancelTokens
, Token
);
679 if (EFI_ERROR (Status
)) {
681 if (Status
== EFI_ABORTED
) {
684 return EFI_NOT_FOUND
;
696 Abort an asynchronous HTTP request or response token.
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.
705 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
706 @param[in] Token Point to storage containing HTTP request or response
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
716 @retval EFI_UNSUPPORTED The implementation does not support this function.
722 IN EFI_HTTP_PROTOCOL
*This
,
723 IN EFI_HTTP_TOKEN
*Token
726 HTTP_PROTOCOL
*HttpInstance
;
729 return EFI_INVALID_PARAMETER
;
732 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
733 ASSERT (HttpInstance
!= NULL
);
735 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
) {
736 return EFI_NOT_STARTED
;
739 return HttpCancel (HttpInstance
, Token
);
744 A callback function to intercept events during message parser.
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.
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().
754 @retval EFI_SUCCESS Continue to parser the message body.
759 HttpBodyParserCallback (
760 IN HTTP_BODY_PARSE_EVENT EventType
,
766 HTTP_TOKEN_WRAP
*Wrap
;
768 if (EventType
!= BodyParseEventOnComplete
) {
772 if (Data
== NULL
|| Length
!= 0 || Context
== NULL
) {
776 Wrap
= (HTTP_TOKEN_WRAP
*) Context
;
777 Wrap
->HttpInstance
->NextMsg
= Data
;
780 // Free Tx4Token or Tx6Token since already received corrsponding HTTP response.
788 The work function of EfiHttpResponse().
790 @param[in] Wrap Pointer to HTTP token's wrap data.
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.
800 IN HTTP_TOKEN_WRAP
*Wrap
804 EFI_HTTP_MESSAGE
*HttpMsg
;
812 CHAR8
*StatusCodeStr
;
814 HTTP_PROTOCOL
*HttpInstance
;
815 EFI_HTTP_TOKEN
*Token
;
817 HTTP_TOKEN_WRAP
*ValueInItem
;
820 if (Wrap
== NULL
|| Wrap
->HttpInstance
== NULL
) {
821 return EFI_INVALID_PARAMETER
;
824 HttpInstance
= Wrap
->HttpInstance
;
825 Token
= Wrap
->HttpToken
;
826 HttpMsg
= Token
->Message
;
828 HttpInstance
->EndofHeader
= NULL
;
829 HttpInstance
->HttpHeaders
= NULL
;
830 HttpMsg
->Headers
= NULL
;
836 if (HttpMsg
->Data
.Response
!= NULL
) {
838 // Need receive the HTTP headers, prepare buffer.
840 Status
= HttpCreateTcpRxEventForHeader (HttpInstance
);
841 if (EFI_ERROR (Status
)) {
846 // Check whether we have cached header from previous call.
848 if ((HttpInstance
->CacheBody
!= NULL
) && (HttpInstance
->NextMsg
!= NULL
)) {
850 // The data is stored at [NextMsg, CacheBody + CacheLen].
852 HdrLen
= HttpInstance
->CacheBody
+ HttpInstance
->CacheLen
- HttpInstance
->NextMsg
;
853 HttpHeaders
= AllocateZeroPool (HdrLen
);
854 if (HttpHeaders
== NULL
) {
855 Status
= EFI_OUT_OF_RESOURCES
;
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
;
868 // Check whether we cached the whole HTTP headers.
870 EndofHeader
= AsciiStrStr (HttpHeaders
, HTTP_END_OF_HDR_STR
);
873 HttpInstance
->EndofHeader
= &EndofHeader
;
874 HttpInstance
->HttpHeaders
= &HttpHeaders
;
876 Status
= HttpTcpReceiveHeader (HttpInstance
, &SizeofHeaders
, &BufferSize
);
877 if (EFI_ERROR (Status
)) {
882 // Cache the part of body.
884 BodyLen
= BufferSize
- (EndofHeader
- HttpHeaders
);
886 if (HttpInstance
->CacheBody
!= NULL
) {
887 FreePool (HttpInstance
->CacheBody
);
890 HttpInstance
->CacheBody
= AllocateZeroPool (BodyLen
);
891 if (HttpInstance
->CacheBody
== NULL
) {
892 Status
= EFI_OUT_OF_RESOURCES
;
896 CopyMem (HttpInstance
->CacheBody
, EndofHeader
, BodyLen
);
897 HttpInstance
->CacheLen
= BodyLen
;
901 // Search for Status Code.
903 StatusCodeStr
= HttpHeaders
+ AsciiStrLen (HTTP_VERSION_STR
) + 1;
904 if (StatusCodeStr
== NULL
) {
908 StatusCode
= AsciiStrDecimalToUintn (StatusCodeStr
);
911 // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
913 Tmp
= AsciiStrStr (HttpHeaders
, HTTP_CRLF_STR
);
918 Tmp
= Tmp
+ AsciiStrLen (HTTP_CRLF_STR
);
919 SizeofHeaders
= SizeofHeaders
- (Tmp
- HttpHeaders
);
920 HeaderTmp
= AllocateZeroPool (SizeofHeaders
);
921 if (HeaderTmp
== NULL
) {
925 CopyMem (HeaderTmp
, Tmp
, SizeofHeaders
);
926 FreePool (HttpHeaders
);
927 HttpHeaders
= HeaderTmp
;
930 // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
932 if (mHttpUtilities
== NULL
) {
933 Status
= EFI_NOT_READY
;
938 // Parse the HTTP header into array of key/value pairs.
940 Status
= mHttpUtilities
->Parse (
945 &HttpMsg
->HeaderCount
947 if (EFI_ERROR (Status
)) {
951 FreePool (HttpHeaders
);
954 HttpMsg
->Data
.Response
->StatusCode
= HttpMappingToStatusCode (StatusCode
);
957 // Init message-body parser by header information.
959 Status
= EFI_NOT_READY
;
961 NetMapRemoveHead (&HttpInstance
->TxTokens
, (VOID
**) &ValueInItem
);
962 if (ValueInItem
== NULL
) {
967 // The first Tx Token not transmitted yet, insert back and return error.
969 if (!ValueInItem
->TcpWrap
.IsTxDone
) {
973 Status
= HttpInitMsgParser (
974 ValueInItem
->TcpWrap
.Method
,
975 HttpMsg
->Data
.Response
->StatusCode
,
976 HttpMsg
->HeaderCount
,
978 HttpBodyParserCallback
,
979 (VOID
*) ValueInItem
,
980 &HttpInstance
->MsgParser
982 if (EFI_ERROR (Status
)) {
987 // Check whether we received a complete HTTP message.
989 if (HttpInstance
->CacheBody
!= NULL
) {
990 Status
= HttpParseMessageBody (HttpInstance
->MsgParser
, HttpInstance
->CacheLen
, HttpInstance
->CacheBody
);
991 if (EFI_ERROR (Status
)) {
995 if (HttpIsMessageComplete (HttpInstance
->MsgParser
)) {
997 // Free the MsgParse since we already have a full HTTP message.
999 HttpFreeMsgParser (HttpInstance
->MsgParser
);
1000 HttpInstance
->MsgParser
= NULL
;
1004 if ((HttpMsg
->Body
== NULL
) || (HttpMsg
->BodyLength
== 0)) {
1005 Status
= EFI_SUCCESS
;
1011 // Receive the response body.
1016 // First check whether we cached some data.
1018 if (HttpInstance
->CacheBody
!= NULL
) {
1020 // Calculate the length of the cached data.
1022 if (HttpInstance
->NextMsg
!= NULL
) {
1024 // We have a cached HTTP message which includes a part of HTTP header of next message.
1026 BodyLen
= HttpInstance
->NextMsg
- (HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
);
1028 BodyLen
= HttpInstance
->CacheLen
- HttpInstance
->CacheOffset
;
1033 // We have some cached data. Just copy the data and return.
1035 if (HttpMsg
->BodyLength
< BodyLen
) {
1036 CopyMem (HttpMsg
->Body
, HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
, HttpMsg
->BodyLength
);
1037 HttpInstance
->CacheOffset
= HttpInstance
->CacheOffset
+ HttpMsg
->BodyLength
;
1040 // Copy all cached data out.
1042 CopyMem (HttpMsg
->Body
, HttpInstance
->CacheBody
+ HttpInstance
->CacheOffset
, BodyLen
);
1043 HttpInstance
->CacheOffset
= BodyLen
+ HttpInstance
->CacheOffset
;
1044 HttpMsg
->BodyLength
= BodyLen
;
1046 if (HttpInstance
->NextMsg
== NULL
) {
1048 // There is no HTTP header of next message. Just free the cache buffer.
1050 FreePool (HttpInstance
->CacheBody
);
1051 HttpInstance
->CacheBody
= NULL
;
1052 HttpInstance
->NextMsg
= NULL
;
1053 HttpInstance
->CacheOffset
= 0;
1057 // Return since we aready received required data.
1059 Status
= EFI_SUCCESS
;
1063 if (BodyLen
== 0 && HttpInstance
->MsgParser
== NULL
) {
1065 // We received a complete HTTP message, and we don't have more data to return to caller.
1067 HttpMsg
->BodyLength
= 0;
1068 Status
= EFI_SUCCESS
;
1073 ASSERT (HttpInstance
->MsgParser
!= NULL
);
1076 // We still need receive more data when there is no cache data and MsgParser is not NULL;
1078 Status
= HttpTcpReceiveBody (Wrap
, HttpMsg
);
1079 if (EFI_ERROR (Status
)) {
1086 Item
= NetMapFindKey (&Wrap
->HttpInstance
->RxTokens
, Wrap
->HttpToken
);
1088 NetMapRemoveItem (&Wrap
->HttpInstance
->RxTokens
, Item
, NULL
);
1090 Token
->Status
= Status
;
1091 gBS
->SignalEvent (Token
->Event
);
1092 HttpCloseTcpRxEvent (Wrap
);
1097 NetMapInsertHead (&HttpInstance
->TxTokens
, ValueInItem
->HttpToken
, ValueInItem
);
1100 HttpTcpTokenCleanup (Wrap
);
1102 if (HttpHeaders
!= NULL
) {
1103 FreePool (HttpHeaders
);
1106 if (HttpMsg
->Headers
!= NULL
) {
1107 FreePool (HttpMsg
->Headers
);
1110 if (HttpInstance
->CacheBody
!= NULL
) {
1111 FreePool (HttpInstance
->CacheBody
);
1112 HttpInstance
->CacheBody
= NULL
;
1115 Token
->Status
= Status
;
1116 gBS
->SignalEvent (Token
->Event
);
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.
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
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.
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.
1151 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
1152 @param[in] Token Pointer to storage containing HTTP response token.
1154 @retval EFI_SUCCESS Allocation succeeded.
1155 @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been
1157 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
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.
1173 IN EFI_HTTP_PROTOCOL
*This
,
1174 IN EFI_HTTP_TOKEN
*Token
1178 EFI_HTTP_MESSAGE
*HttpMsg
;
1179 HTTP_PROTOCOL
*HttpInstance
;
1180 HTTP_TOKEN_WRAP
*Wrap
;
1182 if ((This
== NULL
) || (Token
== NULL
)) {
1183 return EFI_INVALID_PARAMETER
;
1186 HttpMsg
= Token
->Message
;
1187 if (HttpMsg
== NULL
) {
1188 return EFI_INVALID_PARAMETER
;
1191 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
1192 ASSERT (HttpInstance
!= NULL
);
1194 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
) {
1195 return EFI_NOT_STARTED
;
1199 // Check whether the token already existed.
1201 if (EFI_ERROR (NetMapIterate (&HttpInstance
->RxTokens
, HttpTokenExist
, Token
))) {
1202 return EFI_ACCESS_DENIED
;
1205 Wrap
= AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP
));
1207 return EFI_OUT_OF_RESOURCES
;
1210 Wrap
->HttpInstance
= HttpInstance
;
1211 Wrap
->HttpToken
= Token
;
1213 Status
= HttpCreateTcpRxEvent (Wrap
);
1214 if (EFI_ERROR (Status
)) {
1218 Status
= NetMapInsertTail (&HttpInstance
->RxTokens
, Token
, Wrap
);
1219 if (EFI_ERROR (Status
)) {
1224 // If already have pending RxTokens, return directly.
1226 if (NetMapGetCount (&HttpInstance
->RxTokens
) > 1) {
1230 return HttpResponseWorker (Wrap
);
1234 if (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
!= NULL
) {
1235 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx4Token
.CompletionToken
.Event
);
1238 if (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
!= NULL
) {
1239 gBS
->CloseEvent (Wrap
->TcpWrap
.Rx6Token
.CompletionToken
.Event
);
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
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
1258 @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
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.
1270 IN EFI_HTTP_PROTOCOL
*This
1274 HTTP_PROTOCOL
*HttpInstance
;
1277 return EFI_INVALID_PARAMETER
;
1280 HttpInstance
= HTTP_INSTANCE_FROM_PROTOCOL (This
);
1281 ASSERT (HttpInstance
!= NULL
);
1283 if (HttpInstance
->State
!= HTTP_STATE_TCP_CONNECTED
|| (HttpInstance
->Tcp4
== NULL
&&
1284 HttpInstance
->Tcp6
== NULL
)) {
1285 return EFI_NOT_STARTED
;
1288 if (HttpInstance
->LocalAddressIsIPv6
) {
1289 Status
= HttpInstance
->Tcp6
->Poll (HttpInstance
->Tcp6
);
1291 Status
= HttpInstance
->Tcp4
->Poll (HttpInstance
->Tcp4
);