X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=NetworkPkg%2FHttpDxe%2FHttpImpl.c;h=c104b614f35a04c5794ec49ca2a6ea73b4888a29;hb=c9c270193a8f1d64d9664bd191ab57e217206996;hp=553b79cca01aeb35805398e7168fe70c6920a638;hpb=19c25725606b848986f29bb3a216857146fdacb9;p=mirror_edk2.git
diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c
index 553b79cca0..c104b614f3 100644
--- a/NetworkPkg/HttpDxe/HttpImpl.c
+++ b/NetworkPkg/HttpDxe/HttpImpl.c
@@ -1,7 +1,7 @@
/** @file
Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
- Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials
@@ -114,7 +114,6 @@ EfiHttpGetModeData (
@retval EFI_SUCCESS Operation succeeded.
@retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
This is NULL.
- HttpConfigData is NULL.
HttpConfigData->LocalAddressIsIPv6 is FALSE and
HttpConfigData->IPv4Node is NULL.
HttpConfigData->LocalAddressIsIPv6 is TRUE and
@@ -131,7 +130,7 @@ EFI_STATUS
EFIAPI
EfiHttpConfigure (
IN EFI_HTTP_PROTOCOL *This,
- IN EFI_HTTP_CONFIG_DATA *HttpConfigData
+ IN EFI_HTTP_CONFIG_DATA *HttpConfigData OPTIONAL
)
{
HTTP_PROTOCOL *HttpInstance;
@@ -141,9 +140,9 @@ EfiHttpConfigure (
// Check input parameters.
//
if (This == NULL ||
- HttpConfigData == NULL ||
- ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
- (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL))) {
+ (HttpConfigData != NULL &&
+ ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
+ (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) {
return EFI_INVALID_PARAMETER;
}
@@ -152,6 +151,10 @@ EfiHttpConfigure (
if (HttpConfigData != NULL) {
+ if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) {
+ return EFI_UNSUPPORTED;
+ }
+
//
// Now configure this HTTP instance.
//
@@ -176,6 +179,7 @@ EfiHttpConfigure (
sizeof (HttpInstance->IPv4Node)
);
}
+
//
// Creat Tcp child
//
@@ -236,10 +240,12 @@ EfiHttpRequest (
VOID *UrlParser;
EFI_STATUS Status;
CHAR8 *HostName;
+ UINTN HostNameSize;
UINT16 RemotePort;
HTTP_PROTOCOL *HttpInstance;
BOOLEAN Configure;
BOOLEAN ReConfigure;
+ BOOLEAN TlsConfigure;
CHAR8 *RequestMsg;
CHAR8 *Url;
UINTN UrlLen;
@@ -247,151 +253,243 @@ EfiHttpRequest (
HTTP_TOKEN_WRAP *Wrap;
CHAR8 *FileUrl;
UINTN RequestMsgSize;
-
+ EFI_HANDLE ImageHandle;
+
+ //
+ // Initializations
+ //
+ Url = NULL;
+ UrlParser = NULL;
+ RemotePort = 0;
+ HostName = NULL;
+ RequestMsg = NULL;
+ HostNameStr = NULL;
+ Wrap = NULL;
+ FileUrl = NULL;
+ TlsConfigure = FALSE;
+
if ((This == NULL) || (Token == NULL)) {
return EFI_INVALID_PARAMETER;
}
HttpMsg = Token->Message;
- if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) {
+ if (HttpMsg == NULL) {
return EFI_INVALID_PARAMETER;
}
- //
- // Current implementation does not support POST/PUT method.
- // If future version supports these two methods, Request could be NULL for a special case that to send large amounts
- // of data. For this case, the implementation need check whether previous call to Request() has been completed or not.
- //
- //
Request = HttpMsg->Data.Request;
- if ((Request == NULL) || (Request->Url == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
//
- // Only support GET and HEAD method in current implementation.
+ // Only support GET, HEAD, PATCH, PUT and POST method in current implementation.
//
- if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) {
+ if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
+ (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) &&
+ (Request->Method != HttpMethodPost) && (Request->Method != HttpMethodPatch)) {
return EFI_UNSUPPORTED;
}
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
ASSERT (HttpInstance != NULL);
+ //
+ // Capture the method into HttpInstance.
+ //
+ if (Request != NULL) {
+ HttpInstance->Method = Request->Method;
+ }
+
if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
return EFI_NOT_STARTED;
}
- //
- // Check whether the token already existed.
- //
- if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
- return EFI_ACCESS_DENIED;
- }
+ if (Request == NULL) {
+ //
+ // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation)
+ //
+ if ((HttpInstance->Method != HttpMethodPut) &&
+ (HttpInstance->Method != HttpMethodPost) &&
+ (HttpInstance->Method != HttpMethodPatch)) {
+ return EFI_INVALID_PARAMETER;
+ }
- HostName = NULL;
- Wrap = NULL;
- HostNameStr = NULL;
+ //
+ // For PUT/POST/PATCH, we need to have the TCP already configured. Bail out if it is not!
+ //
+ if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
+ return EFI_INVALID_PARAMETER;
+ }
- //
- // Parse the URI of the remote host.
- //
- Url = HttpInstance->Url;
- UrlLen = StrLen (Request->Url) + 1;
- if (UrlLen > HTTP_URL_BUFFER_LEN) {
- Url = AllocateZeroPool (UrlLen);
- if (Url == NULL) {
- return EFI_OUT_OF_RESOURCES;
+ //
+ // We need to have the Message Body for sending the HTTP message across in these cases.
+ //
+ if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {
+ return EFI_INVALID_PARAMETER;
}
- FreePool (HttpInstance->Url);
- HttpInstance->Url = Url;
- }
+ //
+ // Use existing TCP instance to transmit the packet.
+ //
+ Configure = FALSE;
+ ReConfigure = FALSE;
+ } else {
+ //
+ // Check whether the token already existed.
+ //
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
+ return EFI_ACCESS_DENIED;
+ }
- UnicodeStrToAsciiStr (Request->Url, Url);
- UrlParser = NULL;
- Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
+ //
+ // Parse the URI of the remote host.
+ //
+ Url = HttpInstance->Url;
+ UrlLen = StrLen (Request->Url) + 1;
+ if (UrlLen > HTTP_URL_BUFFER_LEN) {
+ Url = AllocateZeroPool (UrlLen);
+ if (Url == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ FreePool (HttpInstance->Url);
+ HttpInstance->Url = Url;
+ }
- RequestMsg = NULL;
- HostName = NULL;
- Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
- if (EFI_ERROR (Status)) {
- RemotePort = HTTP_DEFAULT_PORT;
- }
- //
- // If Configure is TRUE, it indicates the first time to call Request();
- // If ReConfigure is TRUE, it indicates the request URL is not same
- // with the previous call to Request();
- //
- Configure = TRUE;
- ReConfigure = TRUE;
+ UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);
- if (HttpInstance->RemoteHost == NULL) {
//
- // Request() is called the first time.
+ // From the information in Url, the HTTP instance will
+ // be able to determine whether to use http or https.
//
- ReConfigure = FALSE;
- } else {
- if ((HttpInstance->RemotePort == RemotePort) &&
- (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {
+ HttpInstance->UseHttps = IsHttpsUrl (Url);
+
+ //
+ // HTTP is disabled, return directly if the URI is not HTTPS.
+ //
+ if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {
+
+ DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether we need to create Tls child and open the TLS protocol.
+ //
+ if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {
//
- // Host Name and port number of the request URL are the same with previous call to Request().
- // Check whether previous TCP packet sent out.
+ // Use TlsSb to create Tls child and open the TLS protocol.
//
- if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
- //
- // Wrap the HTTP token in HTTP_TOKEN_WRAP
- //
- Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
- if (Wrap == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error1;
- }
+ if (HttpInstance->LocalAddressIsIPv6) {
+ ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;
+ } else {
+ ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;
+ }
- Wrap->HttpToken = Token;
- Wrap->HttpInstance = HttpInstance;
+ HttpInstance->TlsChildHandle = TlsCreateChild (
+ ImageHandle,
+ &(HttpInstance->TlsSb),
+ &(HttpInstance->Tls),
+ &(HttpInstance->TlsConfiguration)
+ );
+ if (HttpInstance->TlsChildHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
- Status = HttpCreateTcpTxEvent (Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
+ TlsConfigure = TRUE;
+ }
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
+ UrlParser = NULL;
+ Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
- Wrap->TcpWrap.Method = Request->Method;
+ HostName = NULL;
+ Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
- FreePool (HostName);
-
+ Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
+ if (EFI_ERROR (Status)) {
+ if (HttpInstance->UseHttps) {
+ RemotePort = HTTPS_DEFAULT_PORT;
+ } else {
+ RemotePort = HTTP_DEFAULT_PORT;
+ }
+ }
+ //
+ // If Configure is TRUE, it indicates the first time to call Request();
+ // If ReConfigure is TRUE, it indicates the request URL is not same
+ // with the previous call to Request();
+ //
+ Configure = TRUE;
+ ReConfigure = TRUE;
+
+ if (HttpInstance->RemoteHost == NULL) {
+ //
+ // Request() is called the first time.
+ //
+ ReConfigure = FALSE;
+ } else {
+ if ((HttpInstance->RemotePort == RemotePort) &&
+ (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) &&
+ (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
+ !TlsConfigure &&
+ HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {
//
- // Queue the HTTP token and return.
+ // Host Name and port number of the request URL are the same with previous call to Request().
+ // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
+ // Check whether previous TCP packet sent out.
//
- return EFI_SUCCESS;
+
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
+ //
+ // Wrap the HTTP token in HTTP_TOKEN_WRAP
+ //
+ Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
+ if (Wrap == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error1;
+ }
+
+ Wrap->HttpToken = Token;
+ Wrap->HttpInstance = HttpInstance;
+
+ Status = HttpCreateTcpTxEvent (Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ Wrap->TcpWrap.Method = Request->Method;
+
+ FreePool (HostName);
+
+ //
+ // Queue the HTTP token and return.
+ //
+ return EFI_SUCCESS;
+ } else {
+ //
+ // Use existing TCP instance to transmit the packet.
+ //
+ Configure = FALSE;
+ ReConfigure = FALSE;
+ }
} else {
//
- // Use existing TCP instance to transmit the packet.
+ // Need close existing TCP instance and create a new TCP instance for data transmit.
//
- Configure = FALSE;
- ReConfigure = FALSE;
- }
- } else {
- //
- // Need close existing TCP instance and create a new TCP instance for data transmit.
- //
- if (HttpInstance->RemoteHost != NULL) {
- FreePool (HttpInstance->RemoteHost);
- HttpInstance->RemoteHost = NULL;
- HttpInstance->RemotePort = 0;
+ if (HttpInstance->RemoteHost != NULL) {
+ FreePool (HttpInstance->RemoteHost);
+ HttpInstance->RemoteHost = NULL;
+ HttpInstance->RemotePort = 0;
+ }
}
}
}
@@ -407,13 +505,14 @@ EfiHttpRequest (
}
if (EFI_ERROR (Status)) {
- HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));
+ HostNameSize = AsciiStrSize (HostName);
+ HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
if (HostNameStr == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error1;
}
- AsciiStrToUnicodeStr (HostName, HostNameStr);
+ AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
if (!HttpInstance->LocalAddressIsIPv6) {
Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
} else {
@@ -426,7 +525,6 @@ EfiHttpRequest (
}
}
-
//
// Save the RemotePort and RemoteHost.
//
@@ -445,6 +543,16 @@ EfiHttpRequest (
} else {
ASSERT (HttpInstance->Tcp6 != NULL);
}
+
+ if (HttpInstance->UseHttps && !TlsConfigure) {
+ Status = TlsCloseSession (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ TlsCloseTxRxEvent (HttpInstance);
+ }
+
HttpCloseConnection (HttpInstance);
EfiHttpCancel (This, NULL);
}
@@ -460,14 +568,21 @@ EfiHttpRequest (
Wrap->HttpToken = Token;
Wrap->HttpInstance = HttpInstance;
- Wrap->TcpWrap.Method = Request->Method;
-
- Status = HttpInitTcp (HttpInstance, Wrap, Configure);
+ if (Request != NULL) {
+ Wrap->TcpWrap.Method = Request->Method;
+ }
+
+ Status = HttpInitSession (
+ HttpInstance,
+ Wrap,
+ Configure || ReConfigure,
+ TlsConfigure
+ );
if (EFI_ERROR (Status)) {
goto Error2;
- }
+ }
- if (!Configure) {
+ if (!Configure && !ReConfigure && !TlsConfigure) {
//
// For the new HTTP token, create TX TCP token events.
//
@@ -481,7 +596,7 @@ EfiHttpRequest (
// Create request message.
//
FileUrl = Url;
- if (*FileUrl != '/') {
+ if (Url != NULL && *FileUrl != '/') {
//
// Convert the absolute-URI to the absolute-path
//
@@ -501,13 +616,23 @@ EfiHttpRequest (
Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
- if (EFI_ERROR (Status)) {
+ if (EFI_ERROR (Status) || NULL == RequestMsg) {
goto Error3;
}
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error4;
+ ASSERT (RequestMsg != NULL);
+
+ //
+ // Every request we insert a TxToken and a response call would remove the TxToken.
+ // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a
+ // continuous request without a response call. So, in such cases, where Request
+ // structure is NULL, we would not insert a TxToken.
+ //
+ if (Request != NULL) {
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error4;
+ }
}
//
@@ -532,7 +657,13 @@ EfiHttpRequest (
return EFI_SUCCESS;
Error5:
+ //
+ // We would have inserted a TxToken only if Request structure is not NULL.
+ // Hence check before we do a remove in this error case.
+ //
+ if (Request != NULL) {
NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
+ }
Error4:
if (RequestMsg != NULL) {
@@ -540,9 +671,14 @@ Error4:
}
Error3:
- HttpCloseConnection (HttpInstance);
+ if (HttpInstance->UseHttps) {
+ TlsCloseSession (HttpInstance);
+ TlsCloseTxRxEvent (HttpInstance);
+ }
Error2:
+ HttpCloseConnection (HttpInstance);
+
HttpCloseTcpConnCloseEvent (HttpInstance);
if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
@@ -554,7 +690,6 @@ Error2:
}
Error1:
-
if (HostName != NULL) {
FreePool (HostName);
}
@@ -588,7 +723,6 @@ HttpCancelTokens (
IN VOID *Context
)
{
-
EFI_HTTP_TOKEN *Token;
HTTP_TOKEN_WRAP *Wrap;
HTTP_PROTOCOL *HttpInstance;
@@ -606,42 +740,33 @@ HttpCancelTokens (
Wrap = (HTTP_TOKEN_WRAP *) Item->Value;
ASSERT (Wrap != NULL);
HttpInstance = Wrap->HttpInstance;
-
- //
- // Free resources.
- //
- NetMapRemoveItem (Map, Item, NULL);
if (!HttpInstance->LocalAddressIsIPv6) {
- if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
- }
-
if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
- }
-
- if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
- FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer);
- }
+ //
+ // Cancle the Token before close its Event.
+ //
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
- } else {
- if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
+ //
+ DispatchDpc ();
}
-
+ } else {
if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
- }
+ //
+ // Cancle the Token before close its Event.
+ //
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
- if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
- FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer);
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
+ //
+ DispatchDpc ();
}
}
-
- FreePool (Wrap);
-
//
// If only one item is to be cancel, return EFI_ABORTED to stop
// iterating the map any more.
@@ -689,22 +814,30 @@ HttpCancel (
}
}
- //
- // Then check the tokens queued by EfiHttpResponse().
- //
- Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
- if (EFI_ERROR (Status)) {
- if (Token != NULL) {
- if (Status == EFI_ABORTED) {
- return EFI_SUCCESS;
+ if (!HttpInstance->UseHttps) {
+ //
+ // Then check the tokens queued by EfiHttpResponse(), except for Https.
+ //
+ Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
+ if (EFI_ERROR (Status)) {
+ if (Token != NULL) {
+ if (Status == EFI_ABORTED) {
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
} else {
- return EFI_NOT_FOUND;
+ return Status;
}
+ }
+ } else {
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
} else {
- return Status;
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
}
}
-
+
return EFI_SUCCESS;
}
@@ -840,6 +973,7 @@ HttpResponseWorker (
NET_MAP_ITEM *Item;
HTTP_TOKEN_WRAP *ValueInItem;
UINTN HdrLen;
+ NET_FRAGMENT Fragment;
if (Wrap == NULL || Wrap->HttpInstance == NULL) {
return EFI_INVALID_PARAMETER;
@@ -856,16 +990,11 @@ HttpResponseWorker (
SizeofHeaders = 0;
BufferSize = 0;
EndofHeader = NULL;
+ ValueInItem = NULL;
+ Fragment.Len = 0;
+ Fragment.Bulk = NULL;
if (HttpMsg->Data.Response != NULL) {
- //
- // Need receive the HTTP headers, prepare buffer.
- //
- Status = HttpCreateTcpRxEventForHeader (HttpInstance);
- if (EFI_ERROR (Status)) {
- goto Error;
- }
-
//
// Check whether we have cached header from previous call.
//
@@ -897,7 +1026,35 @@ HttpResponseWorker (
HttpInstance->EndofHeader = &EndofHeader;
HttpInstance->HttpHeaders = &HttpHeaders;
- Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize);
+
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
if (EFI_ERROR (Status)) {
goto Error;
}
@@ -928,6 +1085,7 @@ HttpResponseWorker (
//
StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
if (StatusCodeStr == NULL) {
+ Status = EFI_NOT_READY;
goto Error;
}
@@ -938,100 +1096,127 @@ HttpResponseWorker (
//
Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
if (Tmp == NULL) {
- goto Error;
- }
-
- Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
- SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
- HeaderTmp = AllocateZeroPool (SizeofHeaders);
- if (HeaderTmp == NULL) {
- goto Error;
- }
-
- CopyMem (HeaderTmp, Tmp, SizeofHeaders);
- FreePool (HttpHeaders);
- HttpHeaders = HeaderTmp;
-
- //
- // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
- //
- if (mHttpUtilities == NULL) {
Status = EFI_NOT_READY;
goto Error;
}
-
+
//
- // Parse the HTTP header into array of key/value pairs.
+ // We could have response with just a HTTP message and no headers. For Example,
+ // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
+ // method. A "\r\n" following Tmp string again would indicate an end. Compare and
+ // set SizeofHeaders to 0.
//
- Status = mHttpUtilities->Parse (
- mHttpUtilities,
- HttpHeaders,
- SizeofHeaders,
- &HttpMsg->Headers,
- &HttpMsg->HeaderCount
- );
- if (EFI_ERROR (Status)) {
- goto Error;
+ Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
+ if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
+ SizeofHeaders = 0;
+ } else {
+ SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
}
- FreePool (HttpHeaders);
- HttpHeaders = NULL;
-
HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
HttpInstance->StatusCode = StatusCode;
- //
- // Init message-body parser by header information.
- //
+
Status = EFI_NOT_READY;
ValueInItem = NULL;
- NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
- if (ValueInItem == NULL) {
- goto Error;
- }
//
- // The first Tx Token not transmitted yet, insert back and return error.
+ // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a
+ // continuous request without a response call. So, we would not do an insert of
+ // TxToken. After we have sent the complete file, we will call a response to get
+ // a final response from server. In such a case, we would not have any TxTokens.
+ // Hence, check that case before doing a NetMapRemoveHead.
//
- if (!ValueInItem->TcpWrap.IsTxDone) {
- goto Error2;
- }
+ if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
+ NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
+ if (ValueInItem == NULL) {
+ goto Error;
+ }
- Status = HttpInitMsgParser (
- ValueInItem->TcpWrap.Method,
- HttpMsg->Data.Response->StatusCode,
- HttpMsg->HeaderCount,
- HttpMsg->Headers,
- HttpBodyParserCallback,
- (VOID *) ValueInItem,
- &HttpInstance->MsgParser
- );
- if (EFI_ERROR (Status)) {
- goto Error2;
+ //
+ // The first Tx Token not transmitted yet, insert back and return error.
+ //
+ if (!ValueInItem->TcpWrap.IsTxDone) {
+ goto Error2;
+ }
}
- //
- // Check whether we received a complete HTTP message.
- //
- if (HttpInstance->CacheBody != NULL) {
- Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
+ if (SizeofHeaders != 0) {
+ HeaderTmp = AllocateZeroPool (SizeofHeaders);
+ if (HeaderTmp == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ CopyMem (HeaderTmp, Tmp, SizeofHeaders);
+ FreePool (HttpHeaders);
+ HttpHeaders = HeaderTmp;
+
+ //
+ // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
+ //
+ if (mHttpUtilities == NULL) {
+ Status = EFI_NOT_READY;
+ goto Error2;
+ }
+
+ //
+ // Parse the HTTP header into array of key/value pairs.
+ //
+ Status = mHttpUtilities->Parse (
+ mHttpUtilities,
+ HttpHeaders,
+ SizeofHeaders,
+ &HttpMsg->Headers,
+ &HttpMsg->HeaderCount
+ );
if (EFI_ERROR (Status)) {
goto Error2;
}
- if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
- //
- // Free the MsgParse since we already have a full HTTP message.
- //
- HttpFreeMsgParser (HttpInstance->MsgParser);
- HttpInstance->MsgParser = NULL;
+ FreePool (HttpHeaders);
+ HttpHeaders = NULL;
+
+
+ //
+ // Init message-body parser by header information.
+ //
+ Status = HttpInitMsgParser (
+ HttpInstance->Method,
+ HttpMsg->Data.Response->StatusCode,
+ HttpMsg->HeaderCount,
+ HttpMsg->Headers,
+ HttpBodyParserCallback,
+ (VOID *) ValueInItem,
+ &HttpInstance->MsgParser
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ //
+ // Check whether we received a complete HTTP message.
+ //
+ if (HttpInstance->CacheBody != NULL) {
+ Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
+ //
+ // Free the MsgParse since we already have a full HTTP message.
+ //
+ HttpFreeMsgParser (HttpInstance->MsgParser);
+ HttpInstance->MsgParser = NULL;
+ }
}
}
- if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
+ if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
Status = EFI_SUCCESS;
goto Exit;
}
- }
+ }
//
// Receive the response body.
@@ -1101,9 +1286,116 @@ HttpResponseWorker (
//
// We still need receive more data when there is no cache data and MsgParser is not NULL;
//
- Status = HttpTcpReceiveBody (Wrap, HttpMsg);
- if (EFI_ERROR (Status)) {
- goto Error;
+ if (!HttpInstance->UseHttps) {
+ Status = HttpTcpReceiveBody (Wrap, HttpMsg);
+
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ } else {
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the body packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ //
+ // Check whether we receive a complete HTTP message.
+ //
+ Status = HttpParseMessageBody (
+ HttpInstance->MsgParser,
+ (UINTN) Fragment.Len,
+ (CHAR8 *) Fragment.Bulk
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
+ //
+ // Free the MsgParse since we already have a full HTTP message.
+ //
+ HttpFreeMsgParser (HttpInstance->MsgParser);
+ HttpInstance->MsgParser = NULL;
+ }
+
+ //
+ // We receive part of header of next HTTP msg.
+ //
+ if (HttpInstance->NextMsg != NULL) {
+ HttpMsg->BodyLength = MIN ((UINTN) HttpInstance->NextMsg - (UINTN) Fragment.Bulk, HttpMsg->BodyLength);
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
+
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
+ if (HttpInstance->CacheLen != 0) {
+ if (HttpInstance->CacheBody != NULL) {
+ FreePool (HttpInstance->CacheBody);
+ }
+
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
+ if (HttpInstance->CacheBody == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
+ HttpInstance->CacheOffset = 0;
+
+ HttpInstance->NextMsg = HttpInstance->CacheBody + ((UINTN) HttpInstance->NextMsg - (UINTN) (Fragment.Bulk + HttpMsg->BodyLength));
+ }
+ } else {
+ HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
+ if (HttpInstance->CacheLen != 0) {
+ if (HttpInstance->CacheBody != NULL) {
+ FreePool (HttpInstance->CacheBody);
+ }
+
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
+ if (HttpInstance->CacheBody == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
+ HttpInstance->CacheOffset = 0;
+ }
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+
+ goto Exit;
}
return Status;
@@ -1126,17 +1418,35 @@ Exit:
return Status;
Error2:
- NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
+ if (ValueInItem != NULL) {
+ NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
+ }
Error:
- HttpTcpTokenCleanup (Wrap);
+ Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
+ }
+
+ if (!HttpInstance->UseHttps) {
+ HttpTcpTokenCleanup (Wrap);
+ } else {
+ FreePool (Wrap);
+ }
if (HttpHeaders != NULL) {
FreePool (HttpHeaders);
+ HttpHeaders = NULL;
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
}
if (HttpMsg->Headers != NULL) {
FreePool (HttpMsg->Headers);
+ HttpMsg->Headers = NULL;
}
if (HttpInstance->CacheBody != NULL) {
@@ -1247,9 +1557,16 @@ EfiHttpResponse (
Wrap->HttpInstance = HttpInstance;
Wrap->HttpToken = Token;
- Status = HttpCreateTcpRxEvent (Wrap);
- if (EFI_ERROR (Status)) {
- goto Error;
+ //
+ // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
+ // receive the https response. A special TlsRxToken is used for receiving TLS
+ // related messages. It should be a blocking response.
+ //
+ if (!HttpInstance->UseHttps) {
+ Status = HttpCreateTcpRxEvent (Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
}
Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);