/** @file | |
RedfishHttpOperation handles HTTP operations. | |
Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "RedfishHttpOperation.h" | |
#include "RedfishHttpData.h" | |
/** | |
This function copies all headers in SrcHeaders to DstHeaders. | |
It's call responsibility to release returned DstHeaders. | |
@param[in] SrcHeaders Source headers. | |
@param[in] SrcHeaderCount Number of header in source headers. | |
@param[out] DstHeaders Destination headers. | |
@param[out] DstHeaderCount Number of header in designation headers. | |
@retval EFI_SUCCESS Headers are copied successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
CopyHttpHeaders ( | |
IN EFI_HTTP_HEADER *SrcHeaders, | |
IN UINTN SrcHeaderCount, | |
OUT EFI_HTTP_HEADER **DstHeaders, | |
OUT UINTN *DstHeaderCount | |
) | |
{ | |
UINTN Index; | |
if ((SrcHeaders == NULL) || (SrcHeaderCount == 0) || (DstHeaders == NULL) || (DstHeaderCount == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*DstHeaderCount = 0; | |
*DstHeaders = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcHeaderCount); | |
if (*DstHeaders == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
*DstHeaderCount = SrcHeaderCount; | |
for (Index = 0; Index < SrcHeaderCount; Index++) { | |
(*DstHeaders)[Index].FieldName = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldName); | |
if ((*DstHeaders)[Index].FieldName == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
(*DstHeaders)[Index].FieldValue = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldValue); | |
if ((*DstHeaders)[Index].FieldValue == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function free resources in Request. Request is no longer available | |
after this function returns successfully. | |
@param[in] Request HTTP request to be released. | |
@retval EFI_SUCCESS Resource is released successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
ReleaseRedfishRequest ( | |
IN REDFISH_REQUEST *Request | |
) | |
{ | |
if (Request == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Request->Headers != NULL) && (Request->HeaderCount > 0)) { | |
HttpFreeHeaderFields (Request->Headers, Request->HeaderCount); | |
Request->Headers = NULL; | |
Request->HeaderCount = 0; | |
} | |
if (Request->Content != NULL) { | |
FreePool (Request->Content); | |
Request->Content = NULL; | |
} | |
if (Request->ContentType != NULL) { | |
FreePool (Request->ContentType); | |
Request->ContentType = NULL; | |
} | |
Request->ContentLength = 0; | |
return EFI_SUCCESS; | |
} | |
/** | |
This function free resources in given Response. | |
@param[in] Response HTTP response to be released. | |
@retval EFI_SUCCESS Resource is released successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
ReleaseRedfishResponse ( | |
IN REDFISH_RESPONSE *Response | |
) | |
{ | |
if (Response == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Response->Headers != NULL) && (Response->HeaderCount > 0)) { | |
HttpFreeHeaderFields (Response->Headers, Response->HeaderCount); | |
Response->Headers = NULL; | |
Response->HeaderCount = 0; | |
} | |
if (Response->Payload != NULL) { | |
ReleaseRedfishPayload (Response->Payload); | |
Response->Payload = NULL; | |
} | |
if (Response->StatusCode != NULL) { | |
FreePool (Response->StatusCode); | |
Response->StatusCode = NULL; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function free resources in given HTTP message. | |
@param[in] HttpMessage HTTP message to be released. | |
@param[in] IsRequest TRUE if this is request type of HTTP message. | |
FALSE if this is response type of HTTP message. | |
@retval EFI_SUCCESS Resource is released successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
ReleaseHttpMessage ( | |
IN EFI_HTTP_MESSAGE *HttpMessage, | |
IN BOOLEAN IsRequest | |
) | |
{ | |
if (HttpMessage == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (IsRequest) { | |
if (HttpMessage->Data.Request != NULL) { | |
if (HttpMessage->Data.Request->Url != NULL) { | |
FreePool (HttpMessage->Data.Request->Url); | |
} | |
FreePool (HttpMessage->Data.Request); | |
HttpMessage->Data.Request = NULL; | |
} | |
} else { | |
if (HttpMessage->Data.Response != NULL) { | |
FreePool (HttpMessage->Data.Response); | |
HttpMessage->Data.Response = NULL; | |
} | |
} | |
if (HttpMessage->Body != NULL) { | |
FreePool (HttpMessage->Body); | |
HttpMessage->Body = NULL; | |
} | |
if (HttpMessage->Headers != NULL) { | |
HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage->HeaderCount); | |
HttpMessage->Headers = NULL; | |
HttpMessage->HeaderCount = 0; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function build Redfish message for sending data to Redfish service. | |
It's call responsibility to properly release returned HTTP message by | |
calling ReleaseHttpMessage. | |
@param[in] ServicePrivate Pointer to Redfish service private data. | |
@param[in] Uri Redfish service URI. | |
@param[in] Method HTTP method. | |
@param[in] Request Additional data to send to Redfish service. | |
This is optional. | |
@param[in] ContentEncoding Content encoding method to compress HTTP context. | |
This is optional. When ContentEncoding is NULL, | |
No compress method will be performed. | |
@retval EFI_HTTP_MESSAGE * Pointer to newly created HTTP message. | |
@retval NULL Error occurred. | |
**/ | |
EFI_HTTP_MESSAGE * | |
BuildRequestMessage ( | |
IN REDFISH_SERVICE_PRIVATE *ServicePrivate, | |
IN EFI_STRING Uri, | |
IN EFI_HTTP_METHOD Method, | |
IN REDFISH_REQUEST *Request OPTIONAL, | |
IN CHAR8 *ContentEncoding OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STRING Url; | |
UINTN UrlSize; | |
UINTN Index; | |
EFI_HTTP_MESSAGE *RequestMsg; | |
EFI_HTTP_REQUEST_DATA *RequestData; | |
UINTN HeaderCount; | |
UINTN HeaderIndex; | |
EFI_HTTP_HEADER *Headers; | |
CHAR8 ContentLengthStr[REDFISH_CONTENT_LENGTH_SIZE]; | |
VOID *Content; | |
UINTN ContentLength; | |
BOOLEAN HasContent; | |
BOOLEAN DoContentEncoding; | |
RequestMsg = NULL; | |
RequestData = NULL; | |
Url = NULL; | |
UrlSize = 0; | |
Content = NULL; | |
ContentLength = 0; | |
HeaderCount = REDFISH_COMMON_HEADER_SIZE; | |
HeaderIndex = 0; | |
Headers = NULL; | |
HasContent = FALSE; | |
DoContentEncoding = FALSE; | |
if ((ServicePrivate == NULL) || (IS_EMPTY_STRING (Uri))) { | |
return NULL; | |
} | |
if (Method >= HttpMethodMax) { | |
return NULL; | |
} | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__, Uri)); | |
// | |
// Build full URL for HTTP query. | |
// | |
UrlSize = (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * sizeof (CHAR16); | |
Url = AllocateZeroPool (UrlSize); | |
if (Url == NULL) { | |
return NULL; | |
} | |
UnicodeSPrint (Url, UrlSize, L"%a%s", ServicePrivate->Host, Uri); | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Url: %s\n", __func__, Url)); | |
// | |
// Step 1: build the HTTP headers. | |
// | |
if (!IS_EMPTY_STRING (ServicePrivate->SessionToken) || !IS_EMPTY_STRING (ServicePrivate->BasicAuth)) { | |
HeaderCount++; | |
} | |
if ((Request != NULL) && (Request->HeaderCount > 0)) { | |
HeaderCount += Request->HeaderCount; | |
} | |
// | |
// Check and see if we will do content encoding or not | |
// | |
if (!IS_EMPTY_STRING (ContentEncoding)) { | |
if (AsciiStrCmp (ContentEncoding, REDFISH_HTTP_CONTENT_ENCODING_NONE) != 0) { | |
DoContentEncoding = TRUE; | |
} | |
} | |
if ((Request != NULL) && !IS_EMPTY_STRING (Request->Content)) { | |
HeaderCount += 2; | |
HasContent = TRUE; | |
if (DoContentEncoding) { | |
HeaderCount += 1; | |
} | |
} | |
Headers = AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER)); | |
if (Headers == NULL) { | |
goto ON_ERROR; | |
} | |
if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_X_AUTH_TOKEN, ServicePrivate->SessionToken); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_AUTHORIZATION, ServicePrivate->BasicAuth); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} | |
if (Request != NULL) { | |
for (Index = 0; Index < Request->HeaderCount; Index++) { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], Request->Headers[Index].FieldName, Request->Headers[Index].FieldValue); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} | |
} | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_HOST, ServicePrivate->HostName); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_ODATA_VERSION_STR, REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_USER_AGENT, REDFISH_HTTP_HEADER_USER_AGENT_VALUE); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_CONNECTION_STR, REDFISH_HTTP_HEADER_CONNECTION_VALUE); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Handle content header | |
// | |
if (HasContent) { | |
if (Request->ContentType == NULL) { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} else { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, Request->ContentType); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} | |
if (Request->ContentLength == 0) { | |
Request->ContentLength = AsciiStrLen (Request->Content); | |
} | |
AsciiSPrint ( | |
ContentLengthStr, | |
sizeof (ContentLengthStr), | |
"%lu", | |
(UINT64)Request->ContentLength | |
); | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_LENGTH, ContentLengthStr); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Encoding | |
// | |
if (DoContentEncoding) { | |
// | |
// We currently only support gzip Content-Encoding. | |
// | |
Status = RedfishContentEncode ( | |
ContentEncoding, | |
Request->Content, | |
Request->ContentLength, | |
&Content, | |
&ContentLength | |
); | |
if (Status == EFI_INVALID_PARAMETER) { | |
DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__)); | |
goto ON_ERROR; | |
} else if (Status == EFI_UNSUPPORTED) { | |
DoContentEncoding = FALSE; | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content coding for %a! Use raw data instead.\n", __func__, ContentEncoding)); | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_IDENTITY); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} else { | |
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_GZIP); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
} | |
} | |
// | |
// When the content is from caller, we use our own copy so that we properly release it later. | |
// | |
if (!DoContentEncoding) { | |
Content = AllocateCopyPool (Request->ContentLength, Request->Content); | |
if (Content == NULL) { | |
goto ON_ERROR; | |
} | |
ContentLength = Request->ContentLength; | |
} | |
} | |
// | |
// Step 2: build the rest of HTTP request info. | |
// | |
RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); | |
if (RequestData == NULL) { | |
goto ON_ERROR; | |
} | |
RequestData->Method = Method; | |
RequestData->Url = Url; | |
// | |
// Step 3: fill in EFI_HTTP_MESSAGE | |
// | |
RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); | |
if (RequestMsg == NULL) { | |
goto ON_ERROR; | |
} | |
ASSERT (HeaderIndex == HeaderCount); | |
RequestMsg->Data.Request = RequestData; | |
RequestMsg->HeaderCount = HeaderIndex; | |
RequestMsg->Headers = Headers; | |
if (HasContent) { | |
RequestMsg->BodyLength = ContentLength; | |
RequestMsg->Body = Content; | |
} | |
return RequestMsg; | |
ON_ERROR: | |
if (Headers != NULL) { | |
HttpFreeHeaderFields (Headers, HeaderIndex); | |
} | |
if (RequestData != NULL) { | |
FreePool (RequestData); | |
} | |
if (RequestMsg != NULL) { | |
FreePool (RequestMsg); | |
} | |
if (Url != NULL) { | |
FreePool (Url); | |
} | |
return NULL; | |
} | |
/** | |
This function parse response message from Redfish service, and | |
build Redfish response for caller. It's call responsibility to | |
properly release Redfish response by calling ReleaseRedfishResponse. | |
@param[in] ServicePrivate Pointer to Redfish service private data. | |
@param[in] ResponseMsg Response message from Redfish service. | |
@param[out] RedfishResponse Redfish response data. | |
@retval EFI_SUCCESS Redfish response is returned successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
ParseResponseMessage ( | |
IN REDFISH_SERVICE_PRIVATE *ServicePrivate, | |
IN EFI_HTTP_MESSAGE *ResponseMsg, | |
OUT REDFISH_RESPONSE *RedfishResponse | |
) | |
{ | |
EFI_STATUS Status; | |
EDKII_JSON_VALUE JsonData; | |
EFI_HTTP_HEADER *ContentEncodedHeader; | |
VOID *DecodedBody; | |
UINTN DecodedLength; | |
if ((ServicePrivate == NULL) || (ResponseMsg == NULL) || (RedfishResponse == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__)); | |
// | |
// Initialization | |
// | |
JsonData = NULL; | |
RedfishResponse->HeaderCount = 0; | |
RedfishResponse->Headers = NULL; | |
RedfishResponse->Payload = NULL; | |
RedfishResponse->StatusCode = NULL; | |
DecodedBody = NULL; | |
DecodedLength = 0; | |
// | |
// Return the HTTP StatusCode. | |
// | |
if (ResponseMsg->Data.Response != NULL) { | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n", __func__, ResponseMsg->Data.Response->StatusCode)); | |
RedfishResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), &ResponseMsg->Data.Response->StatusCode); | |
if (RedfishResponse->StatusCode == NULL) { | |
DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n", __func__)); | |
} | |
} | |
// | |
// Return the HTTP headers. | |
// | |
if (ResponseMsg->Headers != NULL) { | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count: %d\n", __func__, ResponseMsg->HeaderCount)); | |
Status = CopyHttpHeaders ( | |
ResponseMsg->Headers, | |
ResponseMsg->HeaderCount, | |
&RedfishResponse->Headers, | |
&RedfishResponse->HeaderCount | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: Failed to copy HTTP headers: %r\n", __func__, Status)); | |
} | |
} | |
// | |
// Return the HTTP body. | |
// | |
if ((ResponseMsg->BodyLength != 0) && (ResponseMsg->Body != NULL)) { | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length: %d\n", __func__, ResponseMsg->BodyLength)); | |
// | |
// Check if data is encoded. | |
// | |
ContentEncodedHeader = HttpFindHeader (RedfishResponse->HeaderCount, RedfishResponse->Headers, HTTP_HEADER_CONTENT_ENCODING); | |
if (ContentEncodedHeader != NULL) { | |
// | |
// The content is encoded. | |
// | |
Status = RedfishContentDecode ( | |
ContentEncodedHeader->FieldValue, | |
ResponseMsg->Body, | |
ResponseMsg->BodyLength, | |
&DecodedBody, | |
&DecodedLength | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response content: %r decoding method: %a\n.", __func__, Status, ContentEncodedHeader->FieldValue)); | |
goto ON_ERROR; | |
} | |
JsonData = JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL); | |
FreePool (DecodedBody); | |
} else { | |
JsonData = JsonLoadBuffer (ResponseMsg->Body, ResponseMsg->BodyLength, 0, NULL); | |
} | |
if (!JsonValueIsNull (JsonData)) { | |
RedfishResponse->Payload = CreateRedfishPayload (ServicePrivate, JsonData); | |
if (RedfishResponse->Payload == NULL) { | |
DEBUG ((DEBUG_ERROR, "%a: Failed to create payload\n.", __func__)); | |
} | |
JsonValueFree (JsonData); | |
} else { | |
DEBUG ((DEBUG_ERROR, "%a: No payload available\n", __func__)); | |
} | |
} | |
return EFI_SUCCESS; | |
ON_ERROR: | |
if (RedfishResponse != NULL) { | |
ReleaseRedfishResponse (RedfishResponse); | |
} | |
return Status; | |
} | |
/** | |
This function send Redfish request to Redfish service by calling | |
Rest Ex protocol. | |
@param[in] Service Pointer to Redfish service. | |
@param[in] Uri Uri of Redfish service. | |
@param[in] Method HTTP method. | |
@param[in] Request Request data. This is optional. | |
@param[out] Response Redfish response data. | |
@retval EFI_SUCCESS Request is sent and received successfully. | |
@retval Others Errors occur. | |
**/ | |
EFI_STATUS | |
HttpSendReceive ( | |
IN REDFISH_SERVICE Service, | |
IN EFI_STRING Uri, | |
IN EFI_HTTP_METHOD Method, | |
IN REDFISH_REQUEST *Request OPTIONAL, | |
OUT REDFISH_RESPONSE *Response | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STATUS RestExStatus; | |
EFI_HTTP_MESSAGE *RequestMsg; | |
EFI_HTTP_MESSAGE ResponseMsg; | |
REDFISH_SERVICE_PRIVATE *ServicePrivate; | |
EFI_HTTP_HEADER *XAuthTokenHeader; | |
CHAR8 *HttpContentEncoding; | |
if ((Service == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x %s\n", __func__, Method, Uri)); | |
ServicePrivate = (REDFISH_SERVICE_PRIVATE *)Service; | |
if (ServicePrivate->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) { | |
DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | |
return EFI_INVALID_PARAMETER; | |
} | |
ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); | |
HttpContentEncoding = (CHAR8 *)PcdGetPtr (PcdRedfishServiceContentEncoding); | |
RequestMsg = BuildRequestMessage (Service, Uri, Method, Request, HttpContentEncoding); | |
if (RequestMsg == NULL) { | |
DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n", __func__, Uri)); | |
return EFI_PROTOCOL_ERROR; | |
} | |
// | |
// call RESTEx to get response from REST service. | |
// | |
RestExStatus = ServicePrivate->RestEx->SendReceive (ServicePrivate->RestEx, RequestMsg, &ResponseMsg); | |
if (EFI_ERROR (RestExStatus)) { | |
DEBUG ((DEBUG_ERROR, "%a: %s SendReceive failure: %r\n", __func__, Uri, RestExStatus)); | |
} | |
// | |
// Return status code, headers and payload to caller as much as possible even when RestEx returns failure. | |
// | |
Status = ParseResponseMessage (ServicePrivate, &ResponseMsg, Response); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: %s parse response failure: %r\n", __func__, Uri, Status)); | |
} else { | |
// | |
// Capture session token in header | |
// | |
if ((Method == HttpMethodPost) && | |
(Response->StatusCode != NULL) && | |
((*Response->StatusCode == HTTP_STATUS_200_OK) || (*Response->StatusCode == HTTP_STATUS_204_NO_CONTENT))) | |
{ | |
XAuthTokenHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN); | |
if (XAuthTokenHeader != NULL) { | |
Status = UpdateSessionToken (ServicePrivate, XAuthTokenHeader->FieldValue); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n", __func__, Status)); | |
} | |
} | |
} | |
} | |
// | |
// Release resources | |
// | |
if (RequestMsg != NULL) { | |
ReleaseHttpMessage (RequestMsg, TRUE); | |
FreePool (RequestMsg); | |
} | |
ReleaseHttpMessage (&ResponseMsg, FALSE); | |
return RestExStatus; | |
} |