summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/HttpDxe
diff options
context:
space:
mode:
authorNagaraj Hegde <nagaraj-p.hegde@hpe.com>2016-05-06 18:20:00 +0800
committerFu Siyuan <siyuan.fu@intel.com>2016-05-16 14:22:33 +0800
commitd8293d31416242468323ef30c6fa2a5bf6267996 (patch)
tree1087d93ea337f900c8c4fdae71dd7de2da151b82 /NetworkPkg/HttpDxe
parentbfba88bc68eed24c71ff500b740c3f563531d49c (diff)
downloadedk2-d8293d31416242468323ef30c6fa2a5bf6267996.tar.gz
edk2-d8293d31416242468323ef30c6fa2a5bf6267996.tar.bz2
edk2-d8293d31416242468323ef30c6fa2a5bf6267996.zip
NetworkPkg:HttpDxe: Code changes to support HTTP PUT/POST operations
Code changes enables HttpDxe to handle PUT/POST operations. EfiHttpRequest assumes "Request" and "HttpMsg->Headers" can never be NULL. Also, HttpResponseWorker assumes HTTP Reponse will contain headers. We could have response which could contain only a string (HTTP 100 Continue) and no headers. Code changes tries to do-away from these assumptions, which would enable HttpDxe to support PUT/POST operations. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hegde, Nagaraj P nagaraj-p.hegde@hpe.com Reviewed-By: Wu Jiaxin <jiaxin.wu@intel.com> Reviewed-by: Fu Siyuan <siyuan.fu@intel.com>
Diffstat (limited to 'NetworkPkg/HttpDxe')
-rw-r--r--NetworkPkg/HttpDxe/HttpDriver.c3
-rw-r--r--NetworkPkg/HttpDxe/HttpImpl.c423
-rw-r--r--NetworkPkg/HttpDxe/HttpProto.h1
3 files changed, 253 insertions, 174 deletions
diff --git a/NetworkPkg/HttpDxe/HttpDriver.c b/NetworkPkg/HttpDxe/HttpDriver.c
index 2518f4e707..0bde012ebb 100644
--- a/NetworkPkg/HttpDxe/HttpDriver.c
+++ b/NetworkPkg/HttpDxe/HttpDriver.c
@@ -2,6 +2,7 @@
The driver binding and service binding protocol for HttpDxe driver.
Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
@@ -939,6 +940,8 @@ HttpServiceBindingCreateChild (
HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE;
HttpInstance->Service = HttpService;
+ HttpInstance->Method = HttpMethodMax;
+
CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http));
NetMapInit (&HttpInstance->TxTokens);
NetMapInit (&HttpInstance->RxTokens);
diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c
index 7a236f40e0..dd10f826b4 100644
--- a/NetworkPkg/HttpDxe/HttpImpl.c
+++ b/NetworkPkg/HttpDxe/HttpImpl.c
@@ -248,151 +248,185 @@ EfiHttpRequest (
HTTP_TOKEN_WRAP *Wrap;
CHAR8 *FileUrl;
UINTN RequestMsgSize;
-
+
+ //
+ // Initializations
+ //
+ Url = NULL;
+ HostName = NULL;
+ RequestMsg = NULL;
+ HostNameStr = NULL;
+ Wrap = NULL;
+ FileUrl = NULL;
+
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, 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)) {
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 operation (in the current implementation)
+ //
+ if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {
+ return EFI_INVALID_PARAMETER;
+ }
- HostName = NULL;
- Wrap = NULL;
- HostNameStr = NULL;
+ //
+ // For PUT/POST, 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;
+ UnicodeStrToAsciiStr (Request->Url, Url);
+ UrlParser = NULL;
+ Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
- if (HttpInstance->RemoteHost == 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;
+ }
//
- // Request() is called the first time.
+ // 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();
//
- ReConfigure = FALSE;
- } else {
- if ((HttpInstance->RemotePort == RemotePort) &&
- (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {
+ Configure = TRUE;
+ ReConfigure = TRUE;
+
+ if (HttpInstance->RemoteHost == 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.
+ // Request() is called the first time.
//
- if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
+ ReConfigure = FALSE;
+ } else {
+ if ((HttpInstance->RemotePort == RemotePort) &&
+ (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {
//
- // Wrap the HTTP token in HTTP_TOKEN_WRAP
+ // Host Name and port number of the request URL are the same with previous call to Request().
+ // Check whether previous TCP packet sent out.
//
- Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
- if (Wrap == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error1;
- }
- Wrap->HttpToken = Token;
- Wrap->HttpInstance = HttpInstance;
+ 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 = HttpCreateTcpTxEvent (Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
- Wrap->TcpWrap.Method = Request->Method;
+ Wrap->TcpWrap.Method = Request->Method;
- FreePool (HostName);
-
- //
- // Queue the HTTP token and return.
- //
- return EFI_SUCCESS;
+ 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;
+ }
}
}
}
@@ -461,7 +495,9 @@ EfiHttpRequest (
Wrap->HttpToken = Token;
Wrap->HttpInstance = HttpInstance;
- Wrap->TcpWrap.Method = Request->Method;
+ if (Request != NULL) {
+ Wrap->TcpWrap.Method = Request->Method;
+ }
Status = HttpInitTcp (HttpInstance, Wrap, Configure);
if (EFI_ERROR (Status)) {
@@ -482,7 +518,7 @@ EfiHttpRequest (
// Create request message.
//
FileUrl = Url;
- if (*FileUrl != '/') {
+ if (Url != NULL && *FileUrl != '/') {
//
// Convert the absolute-URI to the absolute-path
//
@@ -506,9 +542,17 @@ EfiHttpRequest (
goto Error3;
}
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error4;
+ //
+ // Every request we insert a TxToken and a response call would remove the TxToken.
+ // In cases of PUT/POST, 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;
+ }
}
//
@@ -533,7 +577,13 @@ EfiHttpRequest (
return EFI_SUCCESS;
Error5:
- NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
+ //
+ // 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) {
@@ -970,97 +1020,122 @@ HttpResponseWorker (
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, 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) {
+ 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.
+ //
+ Status = mHttpUtilities->Parse (
+ mHttpUtilities,
+ HttpHeaders,
+ SizeofHeaders,
+ &HttpMsg->Headers,
+ &HttpMsg->HeaderCount
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ 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;
}
- if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
- //
- // Free the MsgParse since we already have a full HTTP message.
- //
- HttpFreeMsgParser (HttpInstance->MsgParser);
- HttpInstance->MsgParser = NULL;
+ //
+ // 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.
diff --git a/NetworkPkg/HttpDxe/HttpProto.h b/NetworkPkg/HttpDxe/HttpProto.h
index 8b47fe00f6..cdad5b0e48 100644
--- a/NetworkPkg/HttpDxe/HttpProto.h
+++ b/NetworkPkg/HttpDxe/HttpProto.h
@@ -91,6 +91,7 @@ typedef struct _HTTP_PROTOCOL {
LIST_ENTRY Link; // Link to all HTTP instance from the service.
BOOLEAN InDestroy;
INTN State;
+ EFI_HTTP_METHOD Method;
UINTN StatusCode;