/** @file\r
Implementation of EFI_HTTP_PROTOCOL protocol interfaces.\r
\r
- Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
\r
This program and the accompanying materials\r
\r
@param[in] This Pointer to EFI_HTTP_PROTOCOL instance.\r
@param[out] HttpConfigData Point to buffer for operational parameters of this\r
- HTTP instance.\r
+ HTTP instance. It is the responsibility of the caller \r
+ to allocate the memory for HttpConfigData and \r
+ HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact, \r
+ it is recommended to allocate sufficient memory to record \r
+ IPv6Node since it is big enough for all possibilities. \r
\r
@retval EFI_SUCCESS Operation succeeded.\r
@retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
This is NULL.\r
HttpConfigData is NULL.\r
- HttpInstance->LocalAddressIsIPv6 is FALSE and\r
- HttpConfigData->IPv4Node is NULL.\r
- HttpInstance->LocalAddressIsIPv6 is TRUE and\r
- HttpConfigData->IPv6Node is NULL.\r
+ HttpConfigData->AccessPoint.IPv4Node or \r
+ HttpConfigData->AccessPoint.IPv6Node is NULL.\r
@retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.\r
\r
**/\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL);\r
\r
- if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
- (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
+ if ((HttpConfigData->AccessPoint.IPv6Node == NULL) ||\r
+ (HttpConfigData->AccessPoint.IPv4Node == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
@retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
This is NULL.\r
HttpConfigData->LocalAddressIsIPv6 is FALSE and\r
- HttpConfigData->IPv4Node is NULL.\r
+ HttpConfigData->AccessPoint.IPv4Node is NULL.\r
HttpConfigData->LocalAddressIsIPv6 is TRUE and\r
- HttpConfigData->IPv6Node is NULL.\r
+ HttpConfigData->AccessPoint.IPv6Node is NULL.\r
@retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling\r
Configure() with NULL to reset it.\r
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
EFIAPI\r
EfiHttpConfigure (\r
IN EFI_HTTP_PROTOCOL *This,\r
- IN EFI_HTTP_CONFIG_DATA *HttpConfigData\r
+ IN EFI_HTTP_CONFIG_DATA *HttpConfigData OPTIONAL\r
) \r
{\r
HTTP_PROTOCOL *HttpInstance;\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL);\r
+ ASSERT (HttpInstance->Service != NULL);\r
\r
if (HttpConfigData != NULL) {\r
\r
+ if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
//\r
// Now configure this HTTP instance.\r
//\r
HTTP_PROTOCOL *HttpInstance;\r
BOOLEAN Configure;\r
BOOLEAN ReConfigure;\r
+ BOOLEAN TlsConfigure;\r
CHAR8 *RequestMsg;\r
CHAR8 *Url;\r
UINTN UrlLen;\r
HTTP_TOKEN_WRAP *Wrap;\r
CHAR8 *FileUrl;\r
UINTN RequestMsgSize;\r
+ EFI_HANDLE ImageHandle;\r
\r
//\r
// Initializations\r
HostNameStr = NULL;\r
Wrap = NULL;\r
FileUrl = NULL;\r
+ TlsConfigure = FALSE;\r
\r
if ((This == NULL) || (Token == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
Request = HttpMsg->Data.Request;\r
\r
//\r
- // Only support GET, HEAD, PUT and POST method in current implementation.\r
+ // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation.\r
//\r
if ((Request != NULL) && (Request->Method != HttpMethodGet) &&\r
- (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost)) {\r
+ (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodDelete) && \r
+ (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost) && \r
+ (Request->Method != HttpMethodPatch)) {\r
return EFI_UNSUPPORTED;\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL);\r
\r
//\r
// Capture the method into HttpInstance.\r
\r
if (Request == NULL) {\r
//\r
- // Request would be NULL only for PUT/POST operation (in the current implementation)\r
+ // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation)\r
//\r
- if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {\r
+ if ((HttpInstance->Method != HttpMethodPut) && \r
+ (HttpInstance->Method != HttpMethodPost) && \r
+ (HttpInstance->Method != HttpMethodPatch)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
//\r
- // For PUT/POST, we need to have the TCP already configured. Bail out if it is not!\r
+ // For PUT/POST/PATCH, we need to have the TCP already configured. Bail out if it is not!\r
//\r
if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {\r
return EFI_INVALID_PARAMETER;\r
\r
\r
UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);\r
+\r
+ //\r
+ // From the information in Url, the HTTP instance will \r
+ // be able to determine whether to use http or https.\r
+ //\r
+ HttpInstance->UseHttps = IsHttpsUrl (Url);\r
+\r
+ //\r
+ // HTTP is disabled, return directly if the URI is not HTTPS.\r
+ //\r
+ if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {\r
+ \r
+ DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Check whether we need to create Tls child and open the TLS protocol.\r
+ //\r
+ if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {\r
+ //\r
+ // Use TlsSb to create Tls child and open the TLS protocol.\r
+ //\r
+ if (HttpInstance->LocalAddressIsIPv6) {\r
+ ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;\r
+ } else {\r
+ ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;\r
+ }\r
+\r
+ HttpInstance->TlsChildHandle = TlsCreateChild (\r
+ ImageHandle,\r
+ &(HttpInstance->TlsSb),\r
+ &(HttpInstance->Tls),\r
+ &(HttpInstance->TlsConfiguration)\r
+ );\r
+ if (HttpInstance->TlsChildHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ TlsConfigure = TRUE;\r
+ }\r
+\r
UrlParser = NULL;\r
Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
if (EFI_ERROR (Status)) {\r
\r
Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
if (EFI_ERROR (Status)) {\r
- RemotePort = HTTP_DEFAULT_PORT;\r
+ if (HttpInstance->UseHttps) {\r
+ RemotePort = HTTPS_DEFAULT_PORT;\r
+ } else {\r
+ RemotePort = HTTP_DEFAULT_PORT;\r
+ }\r
}\r
//\r
// If Configure is TRUE, it indicates the first time to call Request();\r
ReConfigure = FALSE;\r
} else {\r
if ((HttpInstance->RemotePort == RemotePort) &&\r
- (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
+ (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && \r
+ (!HttpInstance->UseHttps || (HttpInstance->UseHttps && \r
+ !TlsConfigure && \r
+ HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {\r
//\r
// Host Name and port number of the request URL are the same with previous call to Request().\r
+ // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.\r
// Check whether previous TCP packet sent out.\r
//\r
\r
\r
FreePool (HostName);\r
\r
+ HttpUrlFreeParser (UrlParser);\r
+\r
//\r
// Queue the HTTP token and return.\r
//\r
\r
FreePool (HostNameStr);\r
if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Error: Could not retrieve the host address from DNS server.\n"));\r
goto Error1;\r
}\r
}\r
} else {\r
ASSERT (HttpInstance->Tcp6 != NULL);\r
}\r
+\r
+ if (HttpInstance->UseHttps && !TlsConfigure) {\r
+ Status = TlsCloseSession (HttpInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error1;\r
+ }\r
+ \r
+ TlsCloseTxRxEvent (HttpInstance);\r
+ }\r
+ \r
HttpCloseConnection (HttpInstance);\r
EfiHttpCancel (This, NULL);\r
}\r
if (Request != NULL) {\r
Wrap->TcpWrap.Method = Request->Method;\r
}\r
-\r
- Status = HttpInitTcp (HttpInstance, Wrap, Configure);\r
+ \r
+ Status = HttpInitSession (\r
+ HttpInstance, \r
+ Wrap, \r
+ Configure || ReConfigure, \r
+ TlsConfigure\r
+ );\r
if (EFI_ERROR (Status)) {\r
goto Error2;\r
- } \r
+ }\r
\r
- if (!Configure) {\r
+ if (!Configure && !ReConfigure && !TlsConfigure) {\r
//\r
// For the new HTTP token, create TX TCP token events. \r
//\r
\r
Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);\r
\r
- if (EFI_ERROR (Status)) {\r
+ if (EFI_ERROR (Status) || NULL == RequestMsg) {\r
goto Error3;\r
}\r
\r
//\r
// Every request we insert a TxToken and a response call would remove the TxToken.\r
- // In cases of PUT/POST, after an initial request-response pair, we would do a\r
+ // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a\r
// continuous request without a response call. So, in such cases, where Request\r
// structure is NULL, we would not insert a TxToken.\r
//\r
if (HostName != NULL) {\r
FreePool (HostName);\r
}\r
+\r
+ if (UrlParser != NULL) {\r
+ HttpUrlFreeParser (UrlParser);\r
+ }\r
\r
return EFI_SUCCESS;\r
\r
} \r
\r
Error3:\r
- HttpCloseConnection (HttpInstance);\r
+ if (HttpInstance->UseHttps) {\r
+ TlsCloseSession (HttpInstance);\r
+ TlsCloseTxRxEvent (HttpInstance);\r
+ }\r
\r
Error2:\r
+ HttpCloseConnection (HttpInstance);\r
+ \r
HttpCloseTcpConnCloseEvent (HttpInstance);\r
if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {\r
gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
if (Wrap != NULL) {\r
FreePool (Wrap);\r
}\r
- if (UrlParser!= NULL) {\r
+ if (UrlParser != NULL) {\r
HttpUrlFreeParser (UrlParser);\r
}\r
\r
}\r
}\r
\r
- //\r
- // Then check the tokens queued by EfiHttpResponse().\r
- //\r
- Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
- if (EFI_ERROR (Status)) {\r
- if (Token != NULL) {\r
- if (Status == EFI_ABORTED) {\r
- return EFI_SUCCESS;\r
+ if (!HttpInstance->UseHttps) {\r
+ //\r
+ // Then check the tokens queued by EfiHttpResponse(), except for Https.\r
+ //\r
+ Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Token != NULL) {\r
+ if (Status == EFI_ABORTED) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return EFI_NOT_FOUND;\r
+ }\r
} else {\r
- return EFI_NOT_FOUND;\r
+ return Status;\r
}\r
+ }\r
+ } else {\r
+ if (!HttpInstance->LocalAddressIsIPv6) {\r
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);\r
} else {\r
- return Status;\r
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);\r
}\r
}\r
-\r
+ \r
return EFI_SUCCESS;\r
}\r
\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL);\r
\r
if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
return EFI_NOT_STARTED;\r
NET_MAP_ITEM *Item;\r
HTTP_TOKEN_WRAP *ValueInItem;\r
UINTN HdrLen;\r
+ NET_FRAGMENT Fragment;\r
\r
if (Wrap == NULL || Wrap->HttpInstance == NULL) {\r
return EFI_INVALID_PARAMETER;\r
BufferSize = 0;\r
EndofHeader = NULL;\r
ValueInItem = NULL;\r
+ Fragment.Len = 0;\r
+ Fragment.Bulk = NULL;\r
\r
if (HttpMsg->Data.Response != NULL) {\r
- //\r
- // Need receive the HTTP headers, prepare buffer.\r
- //\r
- Status = HttpCreateTcpRxEventForHeader (HttpInstance);\r
- if (EFI_ERROR (Status)) {\r
- goto Error;\r
- }\r
-\r
//\r
// Check whether we have cached header from previous call.\r
//\r
ValueInItem = NULL;\r
\r
//\r
- // In cases of PUT/POST, after an initial request-response pair, we would do a\r
+ // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a\r
// continuous request without a response call. So, we would not do an insert of\r
// TxToken. After we have sent the complete file, we will call a response to get\r
// a final response from server. In such a case, we would not have any TxTokens.\r
//\r
// We still need receive more data when there is no cache data and MsgParser is not NULL;\r
//\r
- Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
- if (EFI_ERROR (Status)) {\r
- goto Error2;\r
+ if (!HttpInstance->UseHttps) {\r
+ Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+ \r
+ } else {\r
+ if (HttpInstance->TimeoutEvent == NULL) {\r
+ //\r
+ // Create TimeoutEvent for response\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &HttpInstance->TimeoutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Start the timer, and wait Timeout seconds to receive the body packet.\r
+ //\r
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+ \r
+ Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);\r
+\r
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
+ \r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+\r
+ //\r
+ // Check whether we receive a complete HTTP message.\r
+ //\r
+ Status = HttpParseMessageBody (\r
+ HttpInstance->MsgParser,\r
+ (UINTN) Fragment.Len,\r
+ (CHAR8 *) Fragment.Bulk\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error2;\r
+ }\r
+\r
+ if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
+ //\r
+ // Free the MsgParse since we already have a full HTTP message.\r
+ //\r
+ HttpFreeMsgParser (HttpInstance->MsgParser);\r
+ HttpInstance->MsgParser = NULL;\r
+ }\r
+\r
+ //\r
+ // We receive part of header of next HTTP msg.\r
+ //\r
+ if (HttpInstance->NextMsg != NULL) {\r
+ HttpMsg->BodyLength = MIN ((UINTN) HttpInstance->NextMsg - (UINTN) Fragment.Bulk, HttpMsg->BodyLength);\r
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+ \r
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+ if (HttpInstance->CacheLen != 0) {\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ FreePool (HttpInstance->CacheBody);\r
+ }\r
+ \r
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+ if (HttpInstance->CacheBody == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error2;\r
+ }\r
+ \r
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+ HttpInstance->CacheOffset = 0;\r
+\r
+ HttpInstance->NextMsg = HttpInstance->CacheBody + ((UINTN) HttpInstance->NextMsg - (UINTN) (Fragment.Bulk + HttpMsg->BodyLength));\r
+ }\r
+ } else {\r
+ HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);\r
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+ if (HttpInstance->CacheLen != 0) {\r
+ if (HttpInstance->CacheBody != NULL) {\r
+ FreePool (HttpInstance->CacheBody);\r
+ }\r
+ \r
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+ if (HttpInstance->CacheBody == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error2;\r
+ }\r
+\r
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+ HttpInstance->CacheOffset = 0;\r
+ }\r
+ }\r
+\r
+ if (Fragment.Bulk != NULL) {\r
+ FreePool (Fragment.Bulk);\r
+ Fragment.Bulk = NULL;\r
+ }\r
+\r
+ goto Exit;\r
}\r
\r
return Status;\r
if (Item != NULL) {\r
NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
}\r
- \r
- HttpTcpTokenCleanup (Wrap);\r
+\r
+ if (!HttpInstance->UseHttps) {\r
+ HttpTcpTokenCleanup (Wrap);\r
+ } else {\r
+ FreePool (Wrap);\r
+ }\r
\r
if (HttpHeaders != NULL) {\r
FreePool (HttpHeaders);\r
+ HttpHeaders = NULL;\r
+ }\r
+\r
+ if (Fragment.Bulk != NULL) {\r
+ FreePool (Fragment.Bulk);\r
+ Fragment.Bulk = NULL;\r
}\r
\r
if (HttpMsg->Headers != NULL) {\r
FreePool (HttpMsg->Headers);\r
+ HttpMsg->Headers = NULL;\r
}\r
\r
if (HttpInstance->CacheBody != NULL) {\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL);\r
\r
if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
return EFI_NOT_STARTED;\r
Wrap->HttpInstance = HttpInstance;\r
Wrap->HttpToken = Token;\r
\r
- Status = HttpCreateTcpRxEvent (Wrap);\r
- if (EFI_ERROR (Status)) {\r
- goto Error;\r
+ //\r
+ // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to \r
+ // receive the https response. A special TlsRxToken is used for receiving TLS \r
+ // related messages. It should be a blocking response.\r
+ //\r
+ if (!HttpInstance->UseHttps) {\r
+ Status = HttpCreateTcpRxEvent (Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
}\r
\r
Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);\r
}\r
\r
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);\r
- ASSERT (HttpInstance != NULL);\r
\r
if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {\r
return EFI_NOT_STARTED;\r