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.
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);

View File

@ -249,40 +249,77 @@ EfiHttpRequest (
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;
}
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.
//
@ -290,10 +327,6 @@ EfiHttpRequest (
return EFI_ACCESS_DENIED;
}
HostName = NULL;
Wrap = NULL;
HostNameStr = NULL;
//
// Parse the URI of the remote host.
//
@ -316,7 +349,6 @@ EfiHttpRequest (
goto Error1;
}
RequestMsg = NULL;
HostName = NULL;
Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
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().
// Check whether previous TCP packet sent out.
//
if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
//
// Wrap the HTTP token in HTTP_TOKEN_WRAP
@ -396,6 +429,7 @@ EfiHttpRequest (
}
}
}
}
if (Configure) {
//
@ -461,7 +495,9 @@ EfiHttpRequest (
Wrap->HttpToken = Token;
Wrap->HttpInstance = HttpInstance;
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,10 +542,18 @@ EfiHttpRequest (
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);
if (EFI_ERROR (Status)) {
goto Error4;
}
}
//
// Transmit the request message.
@ -533,7 +577,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) {
@ -970,8 +1020,47 @@ HttpResponseWorker (
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);
if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
SizeofHeaders = 0;
} else {
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);
if (HeaderTmp == NULL) {
goto Error;
@ -1006,27 +1095,12 @@ HttpResponseWorker (
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.
//
if (!ValueInItem->TcpWrap.IsTxDone) {
goto Error2;
}
Status = HttpInitMsgParser (
ValueInItem->TcpWrap.Method,
HttpInstance->Method,
HttpMsg->Data.Response->StatusCode,
HttpMsg->HeaderCount,
HttpMsg->Headers,
@ -1055,6 +1129,7 @@ HttpResponseWorker (
HttpInstance->MsgParser = NULL;
}
}
}
if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
Status = EFI_SUCCESS;

View File

@ -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;