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>
This commit is contained in:
Nagaraj Hegde 2016-05-06 18:20:00 +08:00 committed by Fu Siyuan
parent bfba88bc68
commit d8293d3141
3 changed files with 263 additions and 184 deletions

View File

@ -2,6 +2,7 @@
The driver binding and service binding protocol for HttpDxe driver. The driver binding and service binding protocol for HttpDxe driver.
Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
This program and the accompanying materials This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License 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->Signature = HTTP_PROTOCOL_SIGNATURE;
HttpInstance->Service = HttpService; HttpInstance->Service = HttpService;
HttpInstance->Method = HttpMethodMax;
CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http));
NetMapInit (&HttpInstance->TxTokens); NetMapInit (&HttpInstance->TxTokens);
NetMapInit (&HttpInstance->RxTokens); NetMapInit (&HttpInstance->RxTokens);

View File

@ -249,40 +249,77 @@ EfiHttpRequest (
CHAR8 *FileUrl; CHAR8 *FileUrl;
UINTN RequestMsgSize; UINTN RequestMsgSize;
//
// Initializations
//
Url = NULL;
HostName = NULL;
RequestMsg = NULL;
HostNameStr = NULL;
Wrap = NULL;
FileUrl = NULL;
if ((This == NULL) || (Token == NULL)) { if ((This == NULL) || (Token == NULL)) {
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
} }
HttpMsg = Token->Message; HttpMsg = Token->Message;
if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) { if (HttpMsg == NULL) {
return EFI_INVALID_PARAMETER; 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; 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; return EFI_UNSUPPORTED;
} }
HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
ASSERT (HttpInstance != NULL); ASSERT (HttpInstance != NULL);
//
// Capture the method into HttpInstance.
//
if (Request != NULL) {
HttpInstance->Method = Request->Method;
}
if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
return EFI_NOT_STARTED; return EFI_NOT_STARTED;
} }
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;
}
//
// 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;
}
//
// 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;
}
//
// Use existing TCP instance to transmit the packet.
//
Configure = FALSE;
ReConfigure = FALSE;
} else {
// //
// Check whether the token already existed. // Check whether the token already existed.
// //
@ -290,10 +327,6 @@ EfiHttpRequest (
return EFI_ACCESS_DENIED; return EFI_ACCESS_DENIED;
} }
HostName = NULL;
Wrap = NULL;
HostNameStr = NULL;
// //
// Parse the URI of the remote host. // Parse the URI of the remote host.
// //
@ -316,7 +349,6 @@ EfiHttpRequest (
goto Error1; goto Error1;
} }
RequestMsg = NULL;
HostName = NULL; HostName = NULL;
Status = HttpUrlGetHostName (Url, UrlParser, &HostName); Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
@ -347,6 +379,7 @@ EfiHttpRequest (
// Host Name and port number of the request URL are the same with previous call to Request(). // Host Name and port number of the request URL are the same with previous call to Request().
// Check whether previous TCP packet sent out. // Check whether previous TCP packet sent out.
// //
if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) { if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
// //
// Wrap the HTTP token in HTTP_TOKEN_WRAP // Wrap the HTTP token in HTTP_TOKEN_WRAP
@ -396,6 +429,7 @@ EfiHttpRequest (
} }
} }
} }
}
if (Configure) { if (Configure) {
// //
@ -461,7 +495,9 @@ EfiHttpRequest (
Wrap->HttpToken = Token; Wrap->HttpToken = Token;
Wrap->HttpInstance = HttpInstance; Wrap->HttpInstance = HttpInstance;
if (Request != NULL) {
Wrap->TcpWrap.Method = Request->Method; Wrap->TcpWrap.Method = Request->Method;
}
Status = HttpInitTcp (HttpInstance, Wrap, Configure); Status = HttpInitTcp (HttpInstance, Wrap, Configure);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
@ -482,7 +518,7 @@ EfiHttpRequest (
// Create request message. // Create request message.
// //
FileUrl = Url; FileUrl = Url;
if (*FileUrl != '/') { if (Url != NULL && *FileUrl != '/') {
// //
// Convert the absolute-URI to the absolute-path // Convert the absolute-URI to the absolute-path
// //
@ -506,10 +542,18 @@ EfiHttpRequest (
goto Error3; goto Error3;
} }
//
// 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); Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto Error4; goto Error4;
} }
}
// //
// Transmit the request message. // Transmit the request message.
@ -533,7 +577,13 @@ EfiHttpRequest (
return EFI_SUCCESS; return EFI_SUCCESS;
Error5: 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); NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
}
Error4: Error4:
if (RequestMsg != NULL) { if (RequestMsg != NULL) {
@ -970,8 +1020,47 @@ HttpResponseWorker (
goto Error; goto Error;
} }
//
// 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.
//
Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
SizeofHeaders = 0;
} else {
SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
}
HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
HttpInstance->StatusCode = StatusCode;
Status = EFI_NOT_READY;
ValueInItem = NULL;
//
// 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 (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
if (ValueInItem == NULL) {
goto Error;
}
//
// The first Tx Token not transmitted yet, insert back and return error.
//
if (!ValueInItem->TcpWrap.IsTxDone) {
goto Error2;
}
}
if (SizeofHeaders != 0) {
HeaderTmp = AllocateZeroPool (SizeofHeaders); HeaderTmp = AllocateZeroPool (SizeofHeaders);
if (HeaderTmp == NULL) { if (HeaderTmp == NULL) {
goto Error; goto Error;
@ -1006,27 +1095,12 @@ HttpResponseWorker (
FreePool (HttpHeaders); FreePool (HttpHeaders);
HttpHeaders = NULL; HttpHeaders = NULL;
HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
HttpInstance->StatusCode = StatusCode;
// //
// Init message-body parser by header information. // 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.
//
if (!ValueInItem->TcpWrap.IsTxDone) {
goto Error2;
}
Status = HttpInitMsgParser ( Status = HttpInitMsgParser (
ValueInItem->TcpWrap.Method, HttpInstance->Method,
HttpMsg->Data.Response->StatusCode, HttpMsg->Data.Response->StatusCode,
HttpMsg->HeaderCount, HttpMsg->HeaderCount,
HttpMsg->Headers, HttpMsg->Headers,
@ -1055,6 +1129,7 @@ HttpResponseWorker (
HttpInstance->MsgParser = NULL; HttpInstance->MsgParser = NULL;
} }
} }
}
if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
Status = EFI_SUCCESS; Status = EFI_SUCCESS;

View File

@ -91,6 +91,7 @@ typedef struct _HTTP_PROTOCOL {
LIST_ENTRY Link; // Link to all HTTP instance from the service. LIST_ENTRY Link; // Link to all HTTP instance from the service.
BOOLEAN InDestroy; BOOLEAN InDestroy;
INTN State; INTN State;
EFI_HTTP_METHOD Method;
UINTN StatusCode; UINTN StatusCode;